我正在尝试为我们的注销方法编写单元测试。其中包括FormsAuthentication.SignOut()
。但是,它会抛出System.NullReferenceException
。
我创造了一个模拟; HttpContext
(使用Moq),但显然遗漏了一些东西。
我的模拟上下文包含:
HttpRequestBase
Request
HttpResponseBase
Response
HttpCookieCollection
上的Request.Cookies
和Response.Cookies
上的其他IPrincipal
User
我知道我可以去包装路由并在其中注入一个空的FormsAuth
包装器对象,但我真的想避免另外3个文件来修复一行代码。我仍然很想回答
所以我的问题是“ HttpContext
允许FormsAuthentication.SignOut() to execute.
”
答案 0 :(得分:20)
在这种情况下,NullReferenceException实际上是由调用抛出的:
current.Request.Browser["supportsEmptyStringInCookieValue"]
您可以通过调用:
来测试此断言HttpContext.Current.Request.Browser.SupportsEmptyStringInCookieValue
...这也将返回NullReferenceException。与接受的答案相反,如果您试图致电:
CookielessHelperClass.UseCookieless(current, false, CookieMode)
...从即时窗口,这将返回没有错误。
您可以像这样修复异常:
HttpContext.Current.Request.Browser = new HttpBrowserCapabilities() { Capabilities = new Dictionary<string, string> { { "supportsEmptyStringInCookieValue", "false" } } };
... FormsAuthentication.SignOut()
调用现在将成功。
答案 1 :(得分:13)
您始终可以将FormsAuthentication.SignOut()包装到另一个方法中并对其进行存根/模拟。
创建IFormsAuthenticationWrap界面。
public interface IFormsAuthenticationWrap
{
void SignOut();
}
创建实现IFormsAuthenticationWrap
的包装类public class FormsAuthenticationWrap : IFormsAuthenticationWrap
{
public void SignOut()
{
FormsAuthentication.SignOut();
}
}
你的通话类看起来像这样:
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
}
}
现在让我们来测试一下。您可以使用Moq存根/模拟,但我将在此处显示如何手动执行此操作。 创建存根/模拟类:
public class FormsAuthenticationStub : IFormsAuthenticationWrap
{
public void SignOut()
{
}
}
最后一次写测试:
[TestMethod]
public void TestLogOutMethod()
{
var logOutClass = new LogOutClass(new FormsAuthenticationStub());
logOutClass.LogOutMethod();
}
答案 2 :(得分:8)
这是注销的代码。
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);
}
}
看起来你需要一个CookielessHelperClass实例。太糟糕了它的内部和密封 - 除非你使用TypeMock,否则无法模拟它。包装建议的+1:)
答案 3 :(得分:2)
包装器是干净利落的方式。
你在评论中提到“这将是一个非常大的应用程序”,这是使用包装器的另一个论点,而不是相反。在一个大型应用程序中,您希望拥有明确的依赖关系,并希望轻松完成测试。
您正在交易干净的依赖项,这些依赖项可以通过模糊的依赖项轻松地注入到测试中asp.net的内部工作方式。
另请注意:使用反射器。老实说,我不知道asp.net这个特定部分的内在依赖性,但是你可以用反射器清除任何疑问。
答案 4 :(得分:1)
不要模拟HttpContext,在测试中使用真实的。这样你就不必模拟所有这些Http *的东西了。您可以使用Ivonna直接测试您的方法,而不会模仿所有这些依赖项并获得神秘的异常。