Подтвердить что ты не робот

TaskScheduler.FromCurrentSynchronizationContext - как использовать поток диспетчера WPF при модульном тестировании

У меня есть код в ViewModel, который вызывает службу через задачу. Когда задача завершится, она заполнит ObservableCollection. Проблема в том, что он ждет завершения задачи с помощью метода ContinueWith и предоставления TaskScheduler.FromCurrentSynchronizationContext в качестве планировщика задач, чтобы OC обновлялся в потоке пользовательского интерфейса.

Пока все хорошо, но когда дело доходит до модульного тестирования, оно генерирует исключение, говорящее, что "текущий SynchronizationContext не может использоваться как TaskScheduler". Если я использую mock SynchronizationContext в unit test, тогда ObservableCollection выдает ошибку, потому что она обновляется на основе потока диспетчера.

Есть ли способ обойти это?

Спасибо.

4b9b3361

Ответ 1

Это не совсем просто, но это тоже не так сложно. Что вам нужно сделать, это развернуть рабочий поток, который настроен как STA, и вы запускаете на нем время выполнения Dispatcher. После того, как вы заняты этим рабочим, вы можете отправить ему работу из потоков unit test, которые, очевидно, не инициализированы для такого рода работ. Итак, во-первых, вот как вы запускаете поток диспетчера в своей тестовой настройке:

this.dispatcherThread = new Thread(() =>
{
   // This is here just to force the dispatcher infrastructure to be setup on this thread
   Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
   {
       Trace.WriteLine("Dispatcher worker thread started.");
   }));

   // Run the dispatcher so it starts processing the message loop
   Dispatcher.Run();
});

this.dispatcherThread.SetApartmentState(ApartmentState.STA);
this.dispatcherThread.IsBackground = true;
this.dispatcherThread.Start();

Теперь, если вы хотите полностью закрыть этот поток в своей тестовой очистке, что я вам рекомендую, вы просто выполните следующее:

Dispatcher.FromThread(this.dispatcherThread).InvokeShutdown();

Итак, все, что связано с инфраструктурой, вот все, что вам нужно сделать в своем тесте для выполнения в этом потоке.

public void MyTestMethod
{
    // Kick the test off on the dispatcher worker thread synchronously which will block until the work is competed
    Dispatcher.FromThread(this.dispatcherThread).Invoke(new Action(() =>
    {
        // FromCurrentSynchronizationContext will now resolve to the dispatcher thread here
    }));
}

Ответ 2

Добавьте это в свою тестовую инициализацию, чтобы создать контекст:

SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());

Ответ 3

Это делает его еще проще

TaskScheduler scheduler = Dispatcher.CurrentDispatcher.Invoke(TaskScheduler.FromCurrentSynchronizationContext)

вы можете использовать код scheduler явно (инициализируется с помощью TaskScheduler.FromCurrentSynchronizationContext() во время выполнения)