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

Asp.net mvc HttpContext中需要什么来允许FormsAuthentication.SignOut()执行?,asp.net-mvc,unit-testing,moq,Asp.net Mvc,Unit Testing,Moq,我正在尝试为我们的注销方法编写一个单元测试。除此之外,它还用于身份验证.SignOut()。但是,它会抛出一个System.NullReferenceException 我创造了一个模拟HttpContext(使用Moq),但它显然缺少了一些东西 我的模拟上下文包含: 在请求上模拟的HttpRequestBase 一个模拟的HttpResponseBaseonResponse 通过Request.Cookies上的HttpCookieCollection和Response.Cookies上的另

我正在尝试为我们的注销方法编写一个单元测试。除此之外,它还
用于身份验证.SignOut()
。但是,它会抛出一个
System.NullReferenceException

我创造了一个模拟
HttpContext
(使用Moq),但它显然缺少了一些东西

我的模拟上下文包含:

  • 请求上模拟的
    HttpRequestBase
  • 一个模拟的
    HttpResponseBase
    on
    Response
  • 通过
    Request.Cookies上的
    HttpCookieCollection
    Response.Cookies上的另一个
  • 用户
我知道我可以使用包装器路径,在它的位置插入一个空的
FormsAuth
wrapper对象,但是我真的想避免3个额外的文件,只是为了修复一行代码。我仍然很想得到答案


因此,我的问题是“
HttpContext
中需要什么才能允许执行
FormsAuthentication.SignOut()。

您始终可以将FormsAuthentication.SignOut()包装到另一个方法中并存根/模拟它

创建身份验证包装接口

public interface IFormsAuthenticationWrap
{
    void SignOut();
}
创建实现IFormsAuthenticationWrap的wrap类

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
    }
}
HttpContext.Current.Request.Browser = new HttpBrowserCapabilities() { Capabilities = new Dictionary<string, string> { { "supportsEmptyStringInCookieValue", "false" } } };
现在让我们开始测试。您可以使用Moq进行存根/模拟,但我将在这里演示如何手动执行。 创建存根/模拟类:

public class FormsAuthenticationStub : IFormsAuthenticationWrap
{
    public void SignOut()
    {
    }
}
最后编写测试:

    [TestMethod]
    public void TestLogOutMethod()
    {
        var logOutClass = new LogOutClass(new FormsAuthenticationStub());
        logOutClass.LogOutMethod();
    }

包装是一种干净的方式。

您在评论中提到“这将是一个相当大的应用程序”,这是使用包装器的另一个论点,而不是相反。在大型应用程序中,您希望具有明确的依赖关系,并且希望测试能够轻松完成

您正在交易干净的依赖项,这些依赖项可以轻松地注入到测试中asp.net内部工作的模糊依赖项之上



另一方面:使用反射器。老实说,我不知道asp.net这一特定部分的内部依赖关系,但您可以使用reflector消除任何疑问。

以下是注销代码

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对于包装器建议:)

不要模拟HttpContext,在测试中使用真实的。这样,您就不必模仿所有这些Http*内容。您可以直接使用和测试您的方法,而无需模拟所有这些依赖项并获得神秘的异常。

本例中的NullReferenceException实际上是由调用引发的:

current.Request.Browser["supportsEmptyStringInCookieValue"]
您可以通过调用以下命令来测试此断言:

HttpContext.Current.Request.Browser.SupportsEmptyStringInCookieValue
…这也将返回NullReferenceException。与公认的答案相反,如果您试图拨打:

CookielessHelperClass.UseCookieless(current, false, CookieMode)
…在即时窗口中,此操作将返回,不会出错

您可以按如下方式修复异常:

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
    }
}
HttpContext.Current.Request.Browser = new HttpBrowserCapabilities() { Capabilities = new Dictionary<string, string> { { "supportsEmptyStringInCookieValue", "false" } } };
HttpContext.Current.Request.Browser=new-HttpBrowserCapabilities(){Capabilities=new Dictionary{{“supportsEmptyStringInCookieValue”,“false”}};

…并且
FormsAuthentication.SignOut()
调用现在将成功。

您可以按照我在修改后的答案中向您展示的示例,了解如何打破外部依赖关系。+1正如我在回答中提到的,包装器是一种方法。感谢您的回复。我已经删除了关于“大应用程序”的评论,因为我已经重新措辞了我的问题。显然,不管应用程序大小如何,创建包装器并对此类类/方法使用依赖项注入都是一条可行之路。(如果我想让这个单元测试通过的话,这将是我得到的方法)但是,我仍然想保留我的问题。为反射器上的提示干杯,我会调查的。完美,正是我想要的答案。干杯:)这好像不是答案。。。我需要嘲笑FormsAuthentication,它对我没有帮助。@Yaroslav Yakovlev-仅仅因为有些东西对你没有帮助,你就不应该否决它。这显然有助于原始海报。@Yaroslav Yakovlev不幸的是,你不能嘲笑FormsAuthentication,因为它是一个静态类。这就是为什么womp谈论嘲笑内部(以及由此产生的问题)伟大的答案,它对我帮助很大。不幸的是,我遇到了另一个异常(ArgumentNullException),但也可以通过添加一个浏览器功能“cookies”来修复它。所以最后的答案是下一个:HttpContext.Current.Request.Browser=new-HttpBrowserCapabilities{Capabilities=new Dictionary{{“supportsEmptyStringInCookieValue”,“false”},{“cookies”,“false”},};