HttpContext中需要什么来允许FormsAuthentication.SignOut()执行?

时间:2009-09-15 23:39:12

标签: asp.net-mvc unit-testing moq

我正在尝试为我们的注销方法编写单元测试。其中包括FormsAuthentication.SignOut()。但是,它会抛出System.NullReferenceException

我创造了一个模拟; HttpContext(使用Moq),但显然遗漏了一些东西。

我的模拟上下文包含:

  • HttpRequestBase
  • 上的模拟Request
  • HttpResponseBase
  • 上的模拟Response
  • HttpCookieCollection上的Request.CookiesResponse.Cookies上的其他
  • IPrincipal
  • 上的模拟User

我知道我可以去包装路由并在其中注入一个空的FormsAuth包装器对象,但我真的想避免另外3个文件来修复一行代码。我仍然很想回答

所以我的问题是“ HttpContext允许FormsAuthentication.SignOut() to execute.

需要什么

5 个答案:

答案 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直接测试您的方法,而不会模仿所有这些依赖项并获得神秘的异常。