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; }
    }

    /*  ------   */

}
Другие вопросы по тегам