我正在为MVC 5 Web应用程序编写单元测试。我从测试中 mock 了HttpContext.Current
当运行以下代码形式测试httpSessionStateAfter throw



仅当我运行单元测试时才会发生这种情况。当应用程序运行时,此工作正常。
我正在将Nunit 2.6.3与reshaper测试运行程序一起使用。

 var httpSessionStateBefour = System.Web.HttpContext.Current.Session;
 var Person= await Db.Persons.FirstOrDefaultAsync();
 var httpSessionStateAfter = System.Web.HttpContext.Current.Session;

如何克服这个问题?

这就是我模拟HttpContext的方式
 HttpContext.Current = Fakes.FakeHttpContext();
 HttpContext.Current.Session.Add("IsUserSiteAdmin", true);
 HttpContext.Current.Session.Add("CurrentSite", null);

public static class Fakes
{
    public static HttpContext FakeHttpContext()
    {
        var httpRequest = new HttpRequest("", "http://stackoverflow/", "");
        var stringWriter = new StringWriter();
        var httpResponce = new HttpResponse(stringWriter);
        var httpContext = new HttpContext(httpRequest, httpResponce);

        var sessionContainer = new HttpSessionStateContainer("id", new SessionStateItemCollection(),
            new HttpStaticObjectsCollection(), 10, true,
            HttpCookieMode.AutoDetect,
            SessionStateMode.InProc, false);

        httpContext.Items["AspSession"] = typeof (HttpSessionState).GetConstructor(
            BindingFlags.NonPublic | BindingFlags.Instance,
            null, CallingConventions.Standard,
            new[] {typeof (HttpSessionStateContainer)},
            null)
            .Invoke(new object[] {sessionContainer});

        return httpContext;
    }
}

最佳答案

HttpContext.Current被认为是一个非常糟糕的属性。它不会在ASP.NET主页之外运行。 修复代码的最佳方法是停止查看此属性,并找到一种方法将其与正在测试的代码隔离。 例如,您可以创建一个表示当前 session 数据的接口(interface),并通过需要HTTP上下文的实现将该接口(interface)公开给您正在测试的组件。

根本问题在于HttpContext.Current的工作方式。该属性在ASP.NET框架中是“神奇的”,因为它对于请求-响应操作是唯一的,但是在执行需要时在线程之间跳转-在线程之间有选择地共享。

当您在ASP.NET处理管道之外使用HttpContext.Current时,魔术就消失了。当您像使用异步编程样式一样在此处切换线程时,继续后该属性为null

如果您绝对不能更改代码以消除对HttpContext.Current的硬依赖性,则可以利用本地上下文来作弊此测试:声明连续性时,本地范围内的所有变量都可用于连续性上下文。

// Bring the current value into local scope.
var context = System.Web.HttpContext.Current;

var httpSessionStateBefore = context.Session;
var person = await Db.Persons.FirstOrDefaultAsync();
var httpSessionStateAfter = context.Session;

需要明确的是,对于您当前的情况,这只会起作用。如果在另一个作用域之前在此之前引入await,则该代码将突然再次中断;这是一个简单而又肮脏的答案,我鼓励您忽略并寻求一个更可靠的解决方案。

关于c# - HttpContext.Current在等待后为null(仅在单元测试中),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/26118756/

10-14 17:51
查看更多