我有MVC应用程序,它使用EntityFramework上下文并将其存储在HttpContext.Current.Items中。当HttpContext.Current不可用时,它使用CallContext.SetData将数据存储在当前线程存储中。HttpContext用于web应用程序本身,CallContext用于单元测试,用于存储相同的EF DbContext。
我们也在尝试使用异步\等待,因为我们有大量在它们上中继的库,而且它在web应用程序中运行得很好。但是它在单元测试中失败,因为在线程返回等待块之后CallContext.SetData没有被恢复。下面是这个问题的简化示例:
public async Task Test()
{
ContextUtils.DbContext = new SomeDbContext();
using (ContextUtils.DbContext){
await DoSomeActions();
}
}
public async Task DoSomeActions(){
var data = await new HttpClient().GetAsync(somePage);
// on next line code would fail as ContextUtils.DbContext is null
// as it wasn't synced to new thread that took it
var dbData = ContextUtils.DbContext.SomeTable.First(...);
}所以在这个例子中,ContextUtils.DbContext基本上设置了HttpContext\CallContext.SetData。它对于web应用程序也很好,并且在单元测试中失败,因为SetData不是共享的,而在ContextUtils.DbContext.SomeTable.First(...);行中,DbContext是空的。
我知道我们可以使用CallContext.LogicalSetData\LogicalGetData,它可以与ExecutionContext共享,但是它需要项是可序列化的,并且我不想在尝试序列化它时用序列化属性标记DbContext。
我还看到了斯蒂芬的AsyncEx库(https://github.com/StephenCleary/AsyncEx),它拥有自己的SynchronizationContext,但它要求我更新代码,使用AsyncContext.Run而不是Task.Run,而且我试图避免只为单元测试更新代码。
有什么方法可以在不改变代码本身的情况下修复它,只为了让它在单元测试中工作呢?那么EF DbContext应该存储在单元测试中,而不必将它作为参数传递,并且能够使用异步\等待吗?
谢谢
发布于 2017-07-01 14:15:08
避免使用async void。通过使用Task使测试方法可以等待
[TestMethod]
public async Task Test() {
ContextUtils.DbContext = new SomeDbContext();
using (ContextUtils.DbContext) {
await DoSomeActions();
}
}HttpContext在单元测试期间不可用,因为它绑定到IIS,而后者在单元测试期间不存在。避免将您的代码与HttpContext紧密耦合,将其视为第三方资源,并将其抽象到您可以控制的代码后面。它将使测试、维护和测试代码更容易。考虑审查当前的设计。
https://stackoverflow.com/questions/44861555
复制相似问题