【发布时间】:2013-04-19 17:05:30
【问题描述】:
我们计划在我们的 MVVM 视图模型中使用 async/await,但是在对这段代码进行单元测试时遇到了一个难题。当使用 NUnit 和手写模拟进行消息传递时,我们正在丢失当前的 SynchronizationContext。
最好用下面的小复制示例代码显示:
[Test] public void TestMethod()
{
Func<Task> asyncMethod = async () =>
{
var context = SynchronizationContext.Current;
await TaskEx.Yield();
Assert.AreEqual(context, SynchronizationContext.Current);
};
// Establish the new context
var syncCtx = new SingleThreadSynchronizationContext(false);
SynchronizationContext.SetSynchronizationContext(syncCtx);
// Invoke the function and alert the context to when it completes
var t = asyncMethod();
t.ContinueWith(delegate { syncCtx.Complete(); }, TaskScheduler.Default);
// Pump continuations and propagate any exceptions
syncCtx.RunOnCurrentThread();
t.GetAwaiter().GetResult();
}
实际上大部分代码是从 Stephen Toub on his blog 的 AsyncPump 实现中窃取的。
要通过这个测试,有趣的是在调用异步方法之前输入ExecutionContext.SuppressFlow();。这可能足以解决我们的问题,但我对 ExecutionContext 了解不够,我想更深入地了解正在发生的事情。
为什么await语句生成的代码会吞下当前的SynchronizationContext?
使用单线程上下文对 async/await 代码进行单元测试还有其他明显的方法吗?
PS:我们正在使用 .Net4 和 Microsoft.CompilerServices.AsyncTargetingPack.Net4
PPS:这也发生在使用稳定的 Microsoft.Bcl.Async 而不是 ATP 的简单项目中
【问题讨论】:
-
请升级到
Microsoft.Bcl.Async。 -
我看到 ATP 已经过时了,你认为这是一个错误并在新版本中修复了吗?我现在不能轻易更改版本,也许是星期一...
-
我不确定,但 ATP 是一个非常旧的版本。
-
我相信在单元测试中同步上下文的处理将不同于 UI。我不确定您正在测试的内容实际上会告诉您任何有用的信息。例如在测试中,没有“消息泵”可以封送回,因此框架不一定知道如何将执行注入到原始同步上下文中。
-
升级到 Microsoft.Bcl.Async 没有帮助。
标签: c# .net mvvm nunit async-await