2014-09-05 3 views
1

Я пишу модульные тесты для моделей просмотра. Приложение написано с использованием Caliburn.Micro для поддержки MVVM. Многие модели просмотра зависят от Application.Current.Dispatcher с намерением отправить некоторый код в поток пользовательского интерфейса.Подвеска на Application.Current.Dispatcher.Invoke (действие) при tsing устройства

Чтобы создать объект Application внутри тестов я написал следующий класс:

public class AppInitializer { 
    private static Application app; 
    public static void InitApp() { 
     app = app ?? (app = Application.Current ?? new Application()); 
    } 
} 

Теперь я просто сделайте следующее в каждом тестовом классе:

[ClassInitialize] 
    public static void InitClass(TestContext ctx) { 
     AppInitializer.InitApp(); 
    } 

К сожалению, первый звонок к Application.Current.Dispatcher из модели просмотра зависает мои тесты, пока не будет достигнут тайм-аут.

Я не хочу абстрагироваться каким-то образом Application.CurrentDispatcher, я не хочу пересматривать в модели моделей еще один издевавшийся объект. Я хочу получить обходное решение, если это возможно.

ответ

3

Я думаю, что вам не нужен звонок Application.Run. Вы правы, что одна из обязанностей классов Application заключается в создании и запуске Dispatcher в потоке, который выполняется в данный момент, но все это происходит во время вызова Run.

И вот тут начинаются проблемы: Run - это блокирующий вызов, т. Е. Ваши юнит-тесты не будут выполняться до выхода Run. В Store Apps есть специальный атрибут UITestMethod, но я не думаю, что он доступен в WPF (особенно если вы не используете MSTest).

Итак, каковы ваши варианты? Вы могли бы создать приложение в другом потоке, чем те, которые выполняются вашими модульными тестами, но это вызовет вызовы методов Join, так как вам нужно посмотреть, был ли ваш вызов отправлен в другой поток. Это потенциал для медленных модульных тестов.

Вы даже не можете создать диспетчера вручную в потоке, на котором выполняются ваши юнит-тесты, потому что это то же самое, что упоминалось ранее с классом App: Dispatcher.Run является блокирующим вызовом.

Именно поэтому я бы посоветовал вам создать абстракцию для Dispatcher и ввести ее - это сэкономит вам много боли.

Обновление для окружающей среды Контекст:

В комментариях, я упомянул Ambient контекст как решение, которое не опирается на нагнетание объект в поле зрения модели, которая соответствует Dispatcher абстракции. Вот как это будет выглядеть в коде:

public interface IDispatcher 
{ 
    void ExecuteOnUIThread(Action action); 
    // Add whatever methods you need on this interface 
} 

public static class DispatcherContext 
{ 
    // An instance that implements IDispatcher can be accessed via this static property 
    public static IDispatcher Dispatcher { get; set; } 
} 

// Of course you need to write an adapter for the WPF Dispatcher class 

Таким образом, вы можете создать диспетчерскую макет для модульных тестов, но все еще быть в состоянии получить доступ к этому через статическое свойство в вашей модели представления. Вы можете узнать больше об этом шаблоне на http://blogs.msdn.com/b/ploeh/archive/2007/07/23/ambientcontext.aspx или в отличной книге Марка Семанна Dependency Injection in .NET.

+0

Если я добавлю app.Run(), этот вызов зависает. – EngineerSpock

+0

Жаль, что я еще не закончил свой ответ. Я обновил его. – feO2x

+0

Хммм, к сожалению, я использую везде статический метод Калиберна Execute.OnUITread (Action action). Это будет сложно насмехаться. – EngineerSpock