We´re planning to use async/await in our MVVM view models, but hit a hard issue with unit testing this code. When using NUnit and a hand written mock for our messaging we´re losing the current SynchronizationContext
.
Best shown with following small reproducing example code:
[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();
}
Actually most of this code is stolen from the AsyncPump implementation from Stephen Toub on his blog.
Interestling all needed to make this test pass is tossing in a ExecutionContext.SuppressFlow();
before calling the async method. This could be enough to fix our Problem, but i do not know enough about ExecutionContext and i want some deeper understanding what´s going on.
Why does the code generated by the await statement swallow the current SynchronizationContext?
Is there another obvious way in using a single threaded context for unit testing async/await code?
PS: We´re using .Net4 and Microsoft.CompilerServices.AsyncTargetingPack.Net4
PPS: This also occurs in a simple project using the stable Microsoft.Bcl.Async instead of the ATP