我正在为 MVC 5 网络应用程序编写单元测试。我在测试中模拟了 HttpContext.Current
。
当运行以下代码形成测试 httpSessionStateAfter
throw
System.AggregateException : One or more errors occurred.
----> System.NullReferenceException : Object reference not set to an instance of an object.
只有在我运行单元测试时才会发生这种情况。当应用程序运行时这项工作正常。 我将 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 vNext 做好准备,它更像 OWIN(没有 HttpContext.Current
)。
但是,这可能需要进行大量更改,而您可能还没有做好准备。要正确模拟 HttpContext.Current
,您需要了解它的工作原理。
HttpContext.Current
是一个由 ASP.NET SynchronizationContext
控制的每线程变量。这个SynchronizationContext
是一个“请求上下文”,代表当前的请求;它是在收到新请求时由 ASP.NET 创建的。我有一个 MSDN article on SynchronizationContext
如果您对更多细节感兴趣。
正如我在 async
intro blog post 中解释的那样,当您 await
一个 Task
时,默认情况下它将捕获当前的“上下文”并使用它来恢复 async
方法。当 async
方法在 ASP.NET 请求上下文中运行时,await
捕获的“上下文”是 ASP.NET SynchronizationContext
。当 async
方法恢复时(可能在不同的线程上),ASP.NET SynchronizationContext
将在恢复 之前设置
方法。这就是 HttpContext.Current
异步async
/await
在 ASP.NET 主机中的工作方式。
现在,当您在单元测试中运行相同的代码时,行为会有所不同。具体来说,没有 ASP.NET SynchronizationContext
来设置 HttpContext.Current
。我假设您的单元测试方法返回 Task
,在这种情况下,NUnit 根本不提供 SynchronizationContext
。因此,当 async
方法恢复时(可能在不同的线程上),它的 HttpContext.Current
可能不是同一个。
有几种不同的方法可以解决这个问题。一种选择是编写您自己的 SynchronizationContext
来保留 HttpContext.Current
,就像 ASP.NET 一样。一个更简单(但效率较低)的选项是使用 a SynchronizationContext
that I wrote called AsyncContext
,这确保 async
方法将在同一线程上恢复。您应该能够安装 my AsyncEx
library from NuGet然后将您的单元测试方法包装在对 AsyncContext.Run
的调用中。请注意,单元测试方法现在是同步的:
[Test]
public void MyTest()
{
AsyncContext.Run(async () =>
{
// Test logic goes here: set HttpContext.Current, etc.
});
}
关于c# - 等待后 HttpContext.Current 为空(仅在单元测试中),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26118756/