¿Qué se necesita en el HttpContext para permitir que FormsAuthentication.SignOut () se ejecute?
-
07-07-2019 - |
Pregunta
Estoy tratando de escribir una prueba unitaria para nuestro método de desconexión. Entre otras cosas, FormsAuthentication.SignOut ()
. Sin embargo, arroja un System.NullReferenceException
.
He creado un simulacro; HttpContext
(usando Moq), pero obviamente le falta algo.
Mi contexto simulado contiene:
- Un
HttpRequestBase
burlado enRequest
- Un
HttpResponseBase
burlado enResponse
- Con una
HttpCookieCollection
enRequest.Cookies
y otra enResponse.Cookies
- Un
IPrincipal
burlado enUser
Soy consciente de que podría seguir la ruta del contenedor e inyectar un objeto contenedor vacío FormsAuth
en su lugar, pero realmente me gustaría evitar los 3 archivos adicionales solo para arreglar una línea de código. Eso y todavía tengo curiosidad por una respuesta
Entonces mi pregunta es " Qué se necesita en el HttpContext
para permitir que FormsAuthentication.SignOut () se ejecute.
"
Solución
Aquí está el código para cerrar sesión.
public static void SignOut()
{
Initialize();
HttpContext current = HttpContext.Current;
bool flag = current.CookielessHelper.DoesCookieValueExistInOriginal('F');
current.CookielessHelper.SetCookieValue('F', null);
if (!CookielessHelperClass.UseCookieless(current, false, CookieMode) || current.Request.Browser.Cookies)
{
string str = string.Empty;
if (current.Request.Browser["supportsEmptyStringInCookieValue"] == "false")
{
str = "NoCookie";
}
HttpCookie cookie = new HttpCookie(FormsCookieName, str);
cookie.HttpOnly = true;
cookie.Path = _FormsCookiePath;
cookie.Expires = new DateTime(0x7cf, 10, 12);
cookie.Secure = _RequireSSL;
if (_CookieDomain != null)
{
cookie.Domain = _CookieDomain;
}
current.Response.Cookies.RemoveCookie(FormsCookieName);
current.Response.Cookies.Add(cookie);
}
if (flag)
{
current.Response.Redirect(GetLoginPage(null), false);
}
}
Parece que necesita una instancia de CookielessHelperClass. Lástima que sea interno y esté sellado: no hay forma de burlarse de él a menos que esté usando TypeMock. +1 para sugerencias de envoltura :)
Otros consejos
La excepción NullReferenceException en este caso en realidad está siendo lanzada por la llamada:
current.Request.Browser["supportsEmptyStringInCookieValue"]
Puede probar esta afirmación llamando:
HttpContext.Current.Request.Browser.SupportsEmptyStringInCookieValue
... que también devolverá la NullReferenceException. Contrariamente a la respuesta aceptada, si intenta llamar:
CookielessHelperClass.UseCookieless(current, false, CookieMode)
... desde la ventana inmediata, esto volverá sin error.
Puede solucionar la excepción de esta manera:
HttpContext.Current.Request.Browser = new HttpBrowserCapabilities() { Capabilities = new Dictionary<string, string> { { "supportsEmptyStringInCookieValue", "false" } } };
... y la llamada FormsAuthentication.SignOut ()
ahora tendrá éxito.
Siempre puedes envolver FormsAuthentication.SignOut () en otro método y ponerlo / simularlo.
Crear interfaz IFormsAuthenticationWrap.
public interface IFormsAuthenticationWrap
{
void SignOut();
}
Crear clase de ajuste que implemente IFormsAuthenticationWrap
public class FormsAuthenticationWrap : IFormsAuthenticationWrap
{
public void SignOut()
{
FormsAuthentication.SignOut();
}
}
Tu clase de llamada se verá así:
public class LogOutClass
{
private readonly IFormsAuthenticationWrap _formsAuthentication;
public LogOutClass() : this (new FormsAuthenticationWrap())
{
}
public LogOutClass(IFormsAuthenticationWrap formsAuthentication)
{
_formsAuthentication = formsAuthentication;
}
public void LogOutMethod()
{
// Code before SignOut
_formsAuthentication.SignOut();
// Code after SignOut
}
}
Ahora vamos a nuestra prueba. Puede tropezar / burlarse de Moq, pero aquí le mostraré cómo puede hacerlo manualmente. Cree su clase stub / simulacro:
public class FormsAuthenticationStub : IFormsAuthenticationWrap
{
public void SignOut()
{
}
}
Y el último escribe la prueba:
[TestMethod]
public void TestLogOutMethod()
{
var logOutClass = new LogOutClass(new FormsAuthenticationStub());
logOutClass.LogOutMethod();
}
El contenedor es el camino limpio a seguir.
Usted mencionó en un comentario que "esta va a ser una aplicación bastante grande", ese es otro argumento para usar el contenedor, no al contrario. En una aplicación grande, desea tener dependencias claras y desea que las pruebas se realicen fácilmente.
Usted está intercambiando dependencias limpias que pueden inyectarse fácilmente sobre dependencias oscuras al funcionamiento interno de asp.net en sus pruebas.
En una nota diferente: Usar Reflector . Sinceramente, no conozco las dependencias internas de esta parte específica de asp.net, pero puede aclarar cualquier duda con reflector.
No te burles de HttpContext, usa uno real en tus pruebas. De esta manera no tienes que burlarte de todas estas cosas Http *. Puede usar Ivonna y probar su método directamente, sin burlarse de todas estas dependencias y obtener misteriosas excepciones.