【问题标题】:What is needed in the HttpContext to allow FormsAuthentication.SignOut() to execute?HttpContext 中需要什么来允许 FormsAuthentication.SignOut() 执行?
【发布时间】:2010-11-28 15:32:06
【问题描述】:

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

我创建了一个模拟; HttpContext(使用最小起订量),但它显然缺少一些东西。

我的模拟上下文包含:

  • Request 上的模拟 HttpRequestBase
  • Response 上的模拟 HttpResponseBase
  • Request.Cookies 上有一个HttpCookieCollection,在Response.Cookies 上有另一个
  • User 上的模拟 IPrincipal

我知道我可以走包装器路线并在其位置注入一个空的 FormsAuth 包装器对象,但我真的很想避免 3 个附加文件只是为了修复一行代码。那个,我仍然对答案感到好奇

所以我的问题是“HttpContext 需要什么来允许FormsAuthentication.SignOut() to execute.

【问题讨论】:

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


    【解决方案1】:

    您始终可以将 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();
        }
    

    【讨论】:

    • 您可以按照我在修改后的答案中向您展示的示例如何打破您的外部依赖关系。
    • +1 正如我在回答中提到的那样,包装器是要走的路。
    【解决方案2】:

    包装器是干净的方式。

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

    您正在交易干净的依赖项,这些依赖项可以很容易地通过模糊的依赖项注入到您的测试中的 asp.net 的内部工作中。


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

    【讨论】:

    • 感谢您的回复。我已经删除了关于“大型应用程序”的建议,因为我已经重新措辞了我的问题。显然,无论应用程序大小如何,为此类/方法创建一个包装器并使用依赖注入是 的方法。 (如果我希望通过此单元测试,这将是我得到的方式)但是,我仍然想保留我的问题。为反射器上的提示干杯,我会研究一下。
    【解决方案3】:

    这是退出代码。

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

    【讨论】:

    • 完美,正是我想要的答案。干杯:)
    • its like not an answer ... I need to mock FormsAuthentication and it doesnt 帮帮我。
    • @Yaroslav Yakovlev - 仅仅因为某些事情对你没有帮助,你不应该拒绝它。这显然对原始海报有所帮助。
    • @Yaroslav Yakovlev 不幸的是,您不能模拟 FormsAuthentication,因为它是一个静态类。这就是为什么 womp 谈论嘲笑内部结构(以及那里引起的问题)
    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

      这种情况下的 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() 调用现在将成功。

      【讨论】:

      • 很好的答案,对我帮助很大。不幸的是,我遇到了另一个异常(ArgumentNullException),但也可以通过添加更多浏览器功能 - “cookies”来修复它。所以最后的答案是下一个:HttpContext.Current.Request.Browser = new HttpBrowserCapabilities { Capabilities = new Dictionary { { "supportsEmptyStringInCookieValue", "false" }, { "cookies", "false" }, } };
      猜你喜欢
      • 2022-01-03
      • 1970-01-01
      • 1970-01-01
      • 2022-01-08
      • 2011-11-14
      • 1970-01-01
      • 1970-01-01
      • 2022-11-17
      • 1970-01-01
      相关资源
      最近更新 更多