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();
}
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.
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);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With