Запуск и вызов методов в приложении WPF из проекта модульного тестирования - PullRequest
1 голос
/ 12 июля 2019

Я хотел бы запустить приложение WPF и вызвать методы в ViewModel для управления приложением с целью тестирования интеграции. Что-то вроде:

    [Test]
    public void Test1()
        var application = new MyApp();
        application.InitializeComponent();
        application.Run();

(ОК, это останавливает выполнение теста на этом этапе, предположительно передавая управление приложению WPF. Не знаете, как с этим справиться. Запускать в отдельном потоке или что-то в этом роде?)

Тогда я бы хотел иметь возможность получать и устанавливать значения в ViewModel, что-то вроде этого:

        application.MyViewModel.SomeProperty = "A value!";

Цель здесь - протестировать приложение WPF в интегрированном смысле, не прибегая к WinAppDriver, White, CodedUI или чему-то подобному. Идеи?

Ответы [ 2 ]

2 голосов
/ 12 июля 2019

Вам либо понадобится отдельный поток для управления моделью представления, либо для этого вам потребуется выполнить код в потоке диспетчера.Я предпочитаю последнее, но любой из них будет работать.Первый потребует от вас осторожности при использовании диспетчера для перенаправления некоторых операций в поток пользовательского интерфейса;для просмотра свойств модели это не нужно, потому что WPF сделает это автоматически, но другие вещи, такие как прямые вызовы методов объекта пользовательского интерфейса - например, Window.Close() - do.

Вот пример того, что вы можетеиспользуйте поток диспетчера для выполнения всего кода тестирования:

[TestMethod]
public void TestWpfApp()
{
    Thread thread = new Thread(() =>
    {
        var application = new App();
        Application.ResourceAssembly = System.Reflection.Assembly.GetExecutingAssembly();
        application.InitializeComponent();
        application.Dispatcher.InvokeAsync(() =>
        {
            _TestApplication(application);
        }, System.Windows.Threading.DispatcherPriority.ApplicationIdle);
        application.Run();
    });

    thread.SetApartmentState(ApartmentState.STA);
    thread.Start();
    thread.Join();
}

private static async void _TestApplication(Application application)
{
    Window window = application.MainWindow;
    ViewModel viewModel = (ViewModel)window.DataContext;

    await Task.Delay(TimeSpan.FromSeconds(5));
    viewModel.Text = "Hello World!";
    await Task.Delay(TimeSpan.FromSeconds(5));
    window.Close();
}

Базовая структура состоит в том, чтобы настроить поток, подходящий для запуска пользовательского интерфейса WPF (это должен быть поток STA, и вы не должнывозиться с потоком модульного теста, поэтому для этого необходимо создать новый поток), а затем в этом потоке выполнить обычную настройку WPF плюс очередь через InvokeAsync(), чтобы диспетчер вызвал основной метод тестирования, чтобыон начинает выполняться после запуска кода WPF.

Естественно, в этом примере предполагается, что класс ViewModel имеет свойство Text, а для свойства DataContext главного окна задан экземпляр этого ViewModel.В моем примере программы я просто связал свойство Text со свойством TextBlock.Text.Очевидно, вы можете делать с вашей моделью представления все, что захотите.

Обратите внимание, что мне пришлось явно установить Application.ResourceAssembly.В Visual Studio Community 2017, который я сейчас использую, инфраструктура модульного тестирования запускает текст в контексте, где Assembly.GetEntryAssembly() возвращает null, что нарушает загрузку ресурсов WPF.Установка этого явно исправляет это (я использую Assembly.GetExecutingAssembly(), потому что я поместил код модульного теста в ту же сборку с моей программой WPF примера… очевидно, если вы держите их в разных сборках, вам нужно будет найти правильную сборкукаким-то другим способом).

В моем тестировании использование System.Windows.Threading.DispatcherPriority.ApplicationIdle при вызове Dispatch.InvokeAsync() строго не требовалось.Я обнаружил, что свойства MainWindow и DataContext инициализированы нормально.Но я предпочитаю явно ждать ApplicationIdle, просто чтобы убедиться, что они полностью инициализированы и что сама программа WPF готова начать принимать любые входные данные, которые вы имеете в виду для ваших тестов.

1 голос
/ 12 июля 2019

Нет особого смысла создавать App и вызывать Run() для него в модуле или интеграционном тесте.Вы сможете создать только один App на AppDomain.

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

Это шаблон проектирования, известный как Model-View-ViewModel (MVVM), и рекомендуемый шаблон проектирования, который следует использовать при разработке приложений пользовательского интерфейса на основе XAML.Для этого есть причина - тестирование является одним из них.

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

...