Task.ContinueWith и DispatcherSynchronizationContext
Я столкнулся с проблемой, которую не понимаю при модульном тестировании кода, использующего продолжение задачи и DispatcherSynchrinizationContext.
Код моего юнит-теста:
[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");
}
}
Код, который тестируется:
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;
}
}
Проблема в том, что код, содержащийся в методе "ContinueWith", никогда не достигается, если я укажу "TaskScheduler.FromCurrentSynchronizationContext()". Если я уберу этот параметр, продолжение будет выполнено корректно...
Есть идеи или советы?
2 ответа
Я думаю, что это потому, что, хотя вы создали новый DispatcherSynchronisationContext, нет никакого реального потока, выполняющего цикл диспетчеризации для выполнения вашей задачи.
Попробуйте поставить это в начале вашего теста:
// 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();
}));
Предоставлено: http://reedcopsey.com/2011/11/28/launching-a-wpf-window-in-a-separate-thread-part-1/
Лейн, ты поставил меня на правильный путь, спасибо!!
Я не модифицировал тестируемый код, и вот новая реализация тестового кода, которая работает как положено:
[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; }
}
/* ------ */
}