Использование Dispatcher в тестируемом модулем коде MVVM - PullRequest
4 голосов
/ 24 января 2011

У меня есть приложение MVVM-lite, которое я хотел бы проверить на модуле. Модель использует System.Timers.Timer, поэтому событие обновления заканчивается в фоновом рабочем потоке. Это модульное тестирование хорошо, но во время выполнения вызвала System.NotSupportedException «Этот тип CollectionView не поддерживает изменения в его SourceCollection из потока, отличного от потока Dispatcher». Я надеялся, что MVVM-облегченный класс Threading.DispatcherHelper исправит ситуацию, но вызов DispatcherHelper.CheckBeginInvokeOnUI приведет к сбою моего модульного теста. Вот код, с которым я столкнулся в представлении модель

private void locationChangedHandler(object src, LocationChangedEventArgs e)
{
    if (e.LocationName != this.CurrentPlaceName)
    {
        this.CurrentPlaceName = e.LocationName;
        List<FileInfo> filesTaggedForHere = Tagger.FilesWithTag(this.CurrentPlaceName);

        //This nextline fixes the threading error, but breaks it for unit tests
        //GalaSoft.MvvmLight.Threading.DispatcherHelper.CheckBeginInvokeOnUI(delegate { updateFilesIntendedForHere(filesTaggedForHere); });

        if (Application.Current != null)
        {
            this.dispatcher.Invoke(new Action(delegate { updateFilesIntendedForHere(filesTaggedForHere); }));
        }
        else
        {
            updateFilesIntendedForHere(filesTaggedForHere);
        }
    }
}
private void updateFilesIntendedForHere(List<FileInfo> filesTaggedForHereIn)
{
    this.FilesIntendedForHere.Clear();
    foreach (FileInfo file in filesTaggedForHereIn)
    {
        if (!this.FilesIntendedForHere.Contains(file))
        {
            this.FilesIntendedForHere.Add(file);
        }
    }
}

Я попробовал хитрость в http://kentb.blogspot.com/2009/04/mvvm-infrastructure-viewmodel.html, но вызов Invoke на Dispatcher.CurrentDispatcher не удалось выполнить во время модульного теста, и поэтому он не прошел. Вот почему я вызываю вспомогательный метод напрямую, если запуск выполняется в тесте, а не в приложении.

Это не может быть правдой - ViewModel не должно волновать, откуда он вызывается. Кто-нибудь может понять, почему ни метод диспетчера Кента Бугаарта, ни MVPM-lite DispatcherHelper.CheckBeginInvokeOnUI не работают в моем модульном тесте?

Ответы [ 3 ]

1 голос
/ 12 апреля 2012

Я делаю это так:

class MyViewModel() {
    private readonly SynchronizationContext _syncContext;

    public MyViewModel() {
        _syncContext = SynchronizationContext.Current; // or use DI
    )

    ...

    public void SomeTimerEvent() {
        _syncContext.Post(_ => UpdateUi(), null);
    }
}

Контекстом по умолчанию будет пул потоков в вашем тесте и диспетчер в пользовательском интерфейсе.Вы также можете легко создать свой собственный тестовый контекст, если хотите другое поведение.

0 голосов
/ 18 апреля 2012

Я просто вызываю метод Initialize в моем ViewModelUnitTestBase, и он работает нормально.Убедитесь, что значение DispatcherHelper.UIDispatcher не равно нулю.

public abstract class ViewModelUnitTestBase<T> where T : ViewModelBase
{
    private T _viewModel = default(T);
    public T ViewModel
    {
        get { return _viewModel; }
        set { _viewModel = value; }
    }

    static ViewModelUnitTestBase()
    {
        DispatcherHelper.Initialize();
    }
}
0 голосов
/ 10 апреля 2012

Я не верю, что в MVVM-lite есть простой ответ на этот вопрос.У вас есть правильное решение вызова DispatcherHelper.CheckBeginInvokeOnUI.Но когда выполняется модульный тест, пользовательский интерфейс не существует, и DispatcherHelper не будет работать должным образом.

Я использую ReactiveUI .Это версия DispatcherHelper.CheckBeginInvokeOnUI (RxApp.DeferredScheduler) проверит, чтобы определить, выполняется ли он в модульном тесте.Если это так, он будет работать в текущем потоке, а не пытаться выполнить маршалинг в несуществующий поток пользовательского интерфейса.Вы можете использовать этот код для создания своей собственной проверки в DispatcherHelper.Соответствующий код находится в RxApp.cs метод InUnitTestRunner () (строка 196).Это довольно странно, но работает, и я не думаю, что есть лучший способ.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...