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();
}
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.
来源:https://stackoverflow.com/questions/14087257/how-to-add-synchronization-context-to-async-test-method