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

Ste*_*ter 4 c# mstest async-await

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

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

Pan*_*vos 7

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

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

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


Ste*_*ter 7

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

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

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

using System.Windows.Threading; // WPF Dispatcher from assembly 'WindowsBase'

public static void RunInWpfSyncContext( Func<Task> 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);
  }
}
Run Code Online (Sandbox Code Playgroud)


Ste*_*ary 6

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

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

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