I'm facing an issue that I do not understand when unit testing a code that uses task continuation and DispatcherSynchrinizationContext.
My unit test code :
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext());
var class1 = new Class1();
var result = class1.MyAsyncMethod().Result;
Assert.IsTrue(result == "OK");
}
}
The code that is being tested :
class Class1
{
public Task<string> MyAsyncMethod()
{
var tcs = new TaskCompletionSource<string>();
MyInnerAsyncMethod()
.ContinueWith(t =>
{
// Never reached if TaskScheduler.FromCurrentSynchronizationContext() is set
tcs.SetResult("OK");
}, TaskScheduler.FromCurrentSynchronizationContext());
return tcs.Task;
}
private Task<string> MyInnerAsyncMethod()
{
var tcs = new TaskCompletionSource<string>();
tcs.SetResult("OK");
return tcs.Task;
}
}
The problem is that the code contained within the "ContinueWith" method is never reached IF I specify "TaskScheduler.FromCurrentSynchronizationContext()". If I remove this parameter, the continuation executes correctly ...
Any ideas or advices?
I think this is because although you have created a new DispatcherSynchronisationContext, there is no actual thread running a dispatch loop for your task to execute on.
Try putting this at the beginning of your test:
// Create a thread
Thread newWindowThread = new Thread(new ThreadStart( () =>
{
// Create our context, and install it:
SynchronizationContext.SetSynchronizationContext(
new DispatcherSynchronizationContext(
Dispatcher.CurrentDispatcher));
// Start the Dispatcher Processing
System.Windows.Threading.Dispatcher.Run();
}));
Courtesy of:
http://reedcopsey.com/2011/11/28/launching-a-wpf-window-in-a-separate-thread-part-1/
lain you put me on the right way, thank you !!
I did not modify my code being tested and here is a new implementation of the test code that works as expected :
[TestClass]
public class UnitTest1
{
private ExecutionContext _executionContext;
[TestInitialize]
public void OnSetup()
{
_executionContext = CreateExecutionContext();
SynchronizationContext.SetSynchronizationContext(_executionContext.DispatcherSynchronizationContext);
}
[TestCleanup]
public void OnTearDown()
{
// stops the dispatcher loop
_executionContext.Dispatcher.InvokeShutdown();
}
[TestMethod]
public void TestMethod1()
{
var class1 = new Class1();
var result = class1.MyAsyncMethod().Result;
Assert.IsTrue(result == "OK");
}
/* Helper classes and methods */
private ExecutionContext CreateExecutionContext()
{
var tcs = new TaskCompletionSource<ExecutionContext>();
var mockUIThread = new Thread(() =>
{
// Create the context, and install it:
var dispatcher = Dispatcher.CurrentDispatcher;
var syncContext = new DispatcherSynchronizationContext(dispatcher);
SynchronizationContext.SetSynchronizationContext(syncContext);
tcs.SetResult(new ExecutionContext
{
DispatcherSynchronizationContext = syncContext,
Dispatcher = dispatcher
});
// Start the Dispatcher Processing
Dispatcher.Run();
});
mockUIThread.SetApartmentState(ApartmentState.STA);
mockUIThread.Start();
return tcs.Task.Result;
}
internal class ExecutionContext
{
public DispatcherSynchronizationContext DispatcherSynchronizationContext { get; set; }
public Dispatcher Dispatcher { get; set; }
}
/* ------ */
}