4

我们计划在我们的 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在他的博客上的 AsyncPump 实现中窃取的。

有趣的是,要让这个测试通过,ExecutionContext.SuppressFlow();在调用 async 方法之前要折腾一下。这可能足以解决我们的问题,但我对 ExecutionContext 了解不够,我想更深入地了解正在发生的事情。

为什么 await 语句生成的代码会吞下当前的 SynchronizationContext?
是否有另一种明显的方法可以使用单线程上下文对异步/等待代码进行单元测试?

PS:我们正在使用 .Net4 和 Microsoft.CompilerServices.AsyncTargetingPack.Net4

PPS:这也发生在使用稳定的 Microsoft.Bcl.Async 而不是 ATP 的简单项目中

4

2 回答 2

1

您遇到了 .NET 4.0 中的错误,该错误已在 .NET 4.5 中修复:

SynchronizationContext.Current 在主 UI 线程上的 Continuation 中为 null

这是同一个问题,因为后面的代码await将被包裹在一个延续中。

于 2013-04-20T03:05:50.630 回答
1

我有完全相同的问题。

我发现这是因为我的自定义SynchronizationContext没有正确覆盖和实现CreateCopy方法。似乎异步代码在每个任务(或某事)之后创建了上下文的副本。确保您的也正确实施它。

于 2016-06-23T12:56:56.340 回答