MVVM Light DispatcherHelper и модульное тестирование с MSTest - PullRequest
3 голосов
/ 18 января 2011

Я подумал добавить это в качестве дополнительного вопроса в этой теме:

Модульное тестирование с помощью MVVM Light & DispatcherHelper

но когда я углубился в это, я думаю, что это немного по-другому.

У меня есть один проект, содержащий модели / виды представлений, отдельный проект, содержащий модели, и третий отдельный проект модульных тестов (MSTest), содержащий тесты для моделей. Я использую DispatcherHelper MVVM Light, чтобы помочь мне выполнить работу в фоновом режиме, но чтобы вернуть ошибки из этой задачи обратно в пользовательский интерфейс.

Проект модели представления / представления имеет файл App.xaml.cs, который содержит обработчик события OnStartup, в который я поместил вызов DispatcherHelper.Initialize (). Одна из виртуальных машин вызывает вызов одного из долгосрочных методов модели в другом потоке с помощью делегата. BeginInvoke ():

     FileProcessDelegate processor = GenerateAddressingFile;
     processor.BeginInvoke(FileGenerationComplete, null);

В этом длительном методе он вызывает служебный метод для отправки ошибок обратно в пользовательский интерфейс. Они представлены в форме сообщений mvvm-light, которые отправляются с помощью DispatcherHelper.CheckBeginInvokeOnUI ()

    internal static void ReportErrorToUi(IFileError error)
    {
        DispatcherHelper.CheckBeginInvokeOnUI(
            () => Messenger.Default.Send(new GenericMessage<IFileError>(error), MessageTokens.ReportFileError));
    }

Кажется, что в приложении все работает нормально.

В одном из классов модульного тестирования для модели я пытаюсь проверить, правильно ли работает долгосрочный метод при возникновении ошибок. В этом тестовом классе у меня есть вызов DispatcherHelper.Initialize () в методе инициализации класса MSTest.

    #region Additional test attributes
    // 
    //You can use the following additional attributes as you write your tests:
    //
    //Use ClassInitialize to run code before running the first test in the class
    [ClassInitialize]
    public static void MyClassInitialize(TestContext testContext)
    {
        DispatcherHelper.Initialize();
    }

Тест регистрирует сообщение, которое должно быть отправлено, затем вызывает метод модели напрямую, не вызывая его в отдельном потоке. Насколько я понимаю, поскольку вызов для отправки сообщения обернут в DispatcherHelper.CheckBeginInvokeOnUI () в модели, он должен действовать правильно, независимо от того, происходит ли он в фоновом потоке или в потоке пользовательского интерфейса.

В первом тесте этого класса, который проверяет метод, все работает нормально. Тест получает сообщения об ошибках, отправленные обратно, и проходит с летающими цветами.

Когда последующие тесты в том же классе выполняются, сообщения никогда не будут получены тестом. Когда я прошел через эти последующие тесты, я заметил, что когда я попадаю в метод DispatcherHelper.CheckBeginInvokeOnUI (), экземпляр DispatcherHelper.UIDispatcher.Thread говорит, что он мертв.

Есть мысли?

Ответы [ 2 ]

3 голосов
/ 21 января 2011

Я не уверен, является ли это решение чем-то, что может быть включено в Mvvm Light, или оно нарушает некоторые базовые условия фреймворка, но вот что я сделал, чтобы заставить это работать:

Iсделал пользовательскую локальную версию класса DispatcherHelper, которая имеет одно небольшое отличие от того, что есть в Mvvm Light, а именно логику в методе Initialize ():

    public static void Initialize()
    {
        if (UIDispatcher != null &&
            UIDispatcher.Thread.IsAlive)
        {
            return;
        }
        UIDispatcher = Dispatcher.CurrentDispatcher;
    }

Здесь изменение является добавлением:

&& UIDispatcher.Thread.IsAlive

до состояния.Это позволяет мне вызывать DispatcherHelper.Initialize () в методе MyTestInitialize () моего класса модульного тестирования.Таким образом, если предыдущий поток, с которым был связан UIDispatcher, умер, DispatcherHelper получает новый Dispatcher / Thread, в котором будет выполняться этот текущий метод тестирования.

По-видимому, в среде тестирования VSTS нет "центрального" потока, который действует как поток пользовательского интерфейса в приложении.Метод MyClassInitialize () выполнялся в одном потоке, а затем тесты запускались в совершенно другом потоке.Определение Mvvm Light не позволило бы мне подключить DispatcherHelper к новому тестовому потоку, снова вызвав DispatcherHelper.Initialize (), потому что UIDispatcher не был нулевым, его поток просто мертв.

0 голосов
/ 19 января 2011

Тестирование асинхронного кода сложно.Как правило, если это работает один раз, а потом уже нет, похоже, что вы не синхронизируете свои потоки правильно.В своем методе тестирования (основной поток) вы ожидаете выполнения фонового потока?

Джонас Фоллесо написал несколько хороших статей об асинхронном модульном тестировании в Silverlight, например http://jonas.follesoe.no/2007/09/18/unit-testing-event-based-asynchronous-code/. Обычно этокак я тоже тестирую мой асинхронный код.

Надеюсь, это поможет, Лоран

...