本文介绍了从单元测试项目在WPF应用程序上启动和调用方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想启动WPF应用程序并在ViewModel上调用方法以控制该应用程序,以进行集成测试.像这样:

I would like to launch a WPF app and call methods on the ViewModel to control the app for the purpose of integration testing. Something like:

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

(好的,这会停止测试执行,大概是将控制权传递给WPF应用.不确定如何处理.在单独的线程中启动还是其他?)

(OK, this stops the test execution at this point, presumably passing control to the WPF app. Not sure how to deal with this. Launch in a separate thread or something?)

然后,我希望能够在ViewModel上获取并设置值,如下所示:

Then I'd like to be able to get and set values on the ViewModel, something like this:

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

此处的目标是以集成的方式测试WPF应用程序,而无需使用WinAppDriver,White,CodedUI或类似的杂物.想法?

The goal here is to test the WPF app in an integrated sense without resorting to WinAppDriver, White, CodedUI or anything messy like that. Ideas?

推荐答案

您要么需要一个单独的线程来操作视图模型,要么需要在调度程序线程中执行代码.我更喜欢后者,但是任何一个都可以.前者会要求您在使用分派器将某些操作编组到UI线程时要格外小心;视图模型属性更改不需要这样做,因为WPF会自动为您执行此操作,但其他操作(如直接调用UI对象方法)例如Window.Close()—做.

You'll either need a separate thread to manipulate the view model, or you'll need to execute code in the dispatcher thread to do that. I prefer the latter, but either would work. The former would require you being careful about using the dispatcher to marshal some operations to the UI thread; view model property changes don't need that, because WPF will do that automatically for you, but other things like direct calls to UI object methods — e.g. Window.Close() — do.

下面是一个示例,您可以使用调度程序线程来执行所有测试代码:

Here's an example of what you might do using the dispatcher thread to do all the testing code:

[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 UI的线程(它必须是STA线程,并且您不应该弄乱单元测试的线程,因此为此目的需要创建一个新线程),然后在该线程中,通过InvokeAsync()向调度程序执行通常的WPF设置加队列,以调用主测试方法,以使其在WPF代码开始运行后开始执行.

The basic structure is to set up a thread suitable for running the WPF UI (it has to be an STA thread, and you shouldn't be messing with the unit test's thread, so creating a new thread for this purpose is required), and then in that thread, do the usual WPF setup plus queue via InvokeAsync() to the dispatcher a call to the main testing method, to have it start executing once the WPF code has started running.

自然地,此示例假定具有Text属性的ViewModel类,并且主窗口的DataContext属性设置为此ViewModel的实例.在我的示例程序中,我只是将Text属性绑定到了TextBlock.Text属性.显然,您可以使用视图模型做任何您想做的事情.

Naturally, this example assumes a ViewModel class with a Text property, and the main window's DataContext property set to an instance of this ViewModel. In my sample program, I just bound the Text property to a TextBlock.Text property. Obviously, you could do whatever you want with your view model.

请注意,我必须显式设置Application.ResourceAssembly.在我目前正在使用的Visual Studio Community 2017中,单元测试框架在Assembly.GetEntryAssembly()返回null的上下文中运行文本,这破坏了WPF的资源加载.设置它可以明确地解决此问题(我使用的是Assembly.GetExecutingAssembly(),因为我将单元测试代码与示例WPF程序放在相同的程序集中,显然,如果将它们放在不同的程序集中,则必须找到正确的程序集其他方式).

Note that I had to explicitly set Application.ResourceAssembly. In Visual Studio Community 2017, which is what I'm using at the moment, the unit test framework runs the text in a context where Assembly.GetEntryAssembly() returns null, which breaks WPF's resource loading. Setting it explicitly fixes that (I'm using Assembly.GetExecutingAssembly(), because I put the unit test code in the same assembly with my sample WPF program…obviously, if you keep them in different assemblies, you'd have to find the right assembly some other way).

在我的测试中,并非严格要求在Dispatch.InvokeAsync()的调用中使用System.Windows.Threading.DispatcherPriority.ApplicationIdle.我发现MainWindowDataContext属性初始化良好.但是我更喜欢显式地等待ApplicationIdle,以确保已将它们完全初始化,并且WPF程序本身已准备好开始接受测试时想到的任何输入.

In my testing, using System.Windows.Threading.DispatcherPriority.ApplicationIdle in the call to Dispatch.InvokeAsync() wasn't strictly required. I found the MainWindow and DataContext properties initialized fine. But I prefer to explicitly wait for ApplicationIdle, just to make sure those have been fully initialized and that the WPF program itself is ready to start accepting whatever input you have in mind for your tests.

这篇关于从单元测试项目在WPF应用程序上启动和调用方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-05 23:02