我有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-03 14:35:57
好吧,这里有很多东西。
就我个人而言,我会怀疑使用CallContext.GetData作为HttpContext.Current的后盾,特别是因为您的代码使用了async。考虑使用AsyncLocal代替。但是,AsyncLocal<T>也可能需要序列化。
我还看到了斯蒂芬的AsyncEx库(https://github.com/StephenCleary/AsyncEx),它拥有自己的SynchronizationContext,但它要求我更新代码,使用AsyncContext.Run而不是Task.Run,而且我试图避免只为单元测试更新代码。
这里有几件事:
Task.Run将阻止(非逻辑的)调用上下文以及HttpContext.Current的工作。因此,我假设您的代码没有从DbContext代码中访问Task.Run。听起来你最好的选择就是使用我的AsyncContext。这个类最初是为异步单元测试编写的(早在单元测试框架支持异步单元测试之前)。您根本不需要更新代码;只需在单元测试中使用它:
public void Test()
{
AsyncContext.Run(async () =>
{
ContextUtils.DbContext = new SomeDbContext();
using (ContextUtils.DbContext)
{
await DoSomeActions();
}
});
}发布于 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
复制相似问题