how to add synchronization context to async test method

佐手、 提交于 2019-11-30 22:38:57

Using the informations from Panagiotis Kanavos and Stephen Cleary, I can write my testmethods like this:

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

The inner code now runs in a WPF synchronization context and handles all exceptions as used for MSTest. The Helper method is from 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);
  }
}

You can create a custom SynchronizationContext-derived class and register it as the current context with SynchronizationContext.SetSynchronizationContext. Read Stephen Toub's posts on "Await, SynchronizationContext, and Console Apps" and "Await, SynchronizationContext, and Console Apps: Part 2".

Your custom SynchronizationContext only has to override the Post method that receives callbacks for asynchronous execution. How you execute them is up to you.

The first post provides a synchronization context that stores all posted actions in a queue and a blocking loop that takes actions from the queue and executes them in a single thread.

You can use a single-threaded SynchronizationContext in my AsyncEx library called AsyncContext:

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

However, this does not fully fake a specific UI environment, e.g., Dispatcher.CurrentDispatcher will still be null. If you need that level of faking, you should use the SynchronizationContext implementations from the original Async CTP. It shipped with three SynchronizationContext implementations that could be used for testing: a general-purpose one (similar to my AsyncContext), one for WinForms, and one for WPF.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!