TaskScheduler.FromCurrentSynchronizationContext - как использовать поток диспетчера WPF при модульном тестировании - PullRequest
18 голосов
/ 10 ноября 2011

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

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

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

Спасибо.

Ответы [ 3 ]

17 голосов
/ 14 ноября 2011

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

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
    }));
}
14 голосов
/ 02 июня 2013

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

SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
0 голосов
/ 05 декабря 2016

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

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

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

...