如何将同步上下文添加到异步测试方法

我有Visual Studio 2012和需要同步上下文的异步测试。
但是MSTest的默认同步上下文为null。
我想测试在具有同步上下文的WPF或WinForms-UI线程上运行。
将SynchronizationContext添加到测试线程的最佳方法是什么?

[TestMethod] public async Task MyTest() { Assert.IsNotNull( SynchronizationContext.Current ); await MyTestAsync(); DoSomethingOnTheSameThread(); } 

您可以创建自定义SynchronizationContext派生类,并使用SynchronizationContext.SetSynchronizationContext将其注册为当前上下文。 阅读Stephen Toub关于“ Await,SynchronizationContext和Console Apps ”和“ Await,SynchronizationContext和Console Apps:Part 2 ”的post。

您的自定义SynchronizationContext只需覆盖接收异步执行回调的Post方法。 你如何执行它们取决于你。

第一篇文章提供了一个同步上下文,它将所有发布的操作存储在一个队列中,一个阻塞循环从队列中获取操作并在单个线程中执行它们。

您可以在我的AsyncEx库中使用名为AsyncContext的单线程SynchronizationContext

 [TestMethod] public void MyTest() { AsyncContext.Run(async () => { Assert.IsNotNull( SynchronizationContext.Current ); await MyTestAsync(); DoSomethingOnTheSameThread(); }); } 

但是,这并不能完全伪造特定的UI环境,例如, Dispatcher.CurrentDispatcher仍然为null 。 如果您需要这种级别的伪造,则应使用原始Async CTP中的SynchronizationContext实现。 它附带了三个可用于测试的SynchronizationContext实现:一个通用的(类似于我的AsyncContext ),一个用于WinForms,一个用于WPF。

使用Panagiotis Kanavos和Stephen Cleary的信息,我可以像这样编写我的测试方法:

  [TestMethod] public void MyTest() { Helper.RunInWpfSyncContext( async () => { Assert.IsNotNull( SynchronizationContext.Current ); await MyTestAsync(); DoSomethingOnTheSameThread(); }); } 

内部代码现在在WPF同步上下文中运行,并处理用于MSTest的所有exception。 Helper方法来自Stephen Toub:

 using System.Windows.Threading; // WPF Dispatcher from assembly 'WindowsBase' public static void RunInWpfSyncContext( Func function ) { if (function == null) throw new ArgumentNullException("function"); var prevCtx = SynchronizationContext.Current; try { var syncCtx = new DispatcherSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(syncCtx); var task = function(); if (task == null) throw new InvalidOperationException(); var frame = new DispatcherFrame(); var t2 = task.ContinueWith(x=>{frame.Continue = false;}, TaskScheduler.Default); Dispatcher.PushFrame(frame); // execute all tasks until frame.Continue == false task.GetAwaiter().GetResult(); // rethrow exception when task has failed } finally { SynchronizationContext.SetSynchronizationContext(prevCtx); } }