Search code examples
c#mstestasync-await

how to add synchronization context to async test method


I have Visual Studio 2012 and an asynchronous test that needs a synchronization context.
But the default synchronization context of MSTest is null.
I would like to test as running on a WPF- or WinForms-UI thread that has a synchronization context.
What's the best method to add a SynchronizationContext to the test thread ?

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

Solution

  • 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);
      }
    }