如何在 NUnit 测试中为 WPF 文本框调用 setter

How to Invoke a setter for a WPF Textbox in an NUnit Test

我尝试在 NUnit 中编写系统测试,我想使用来自 ms.UI 的自动化来调用 UI。

出于某种原因,我的调用失败了 - 我在网上发现了一些提示,这些提示使我进入了可以编写编译测试但断言失败的状态。

这是一个可编译的最小示例。我的问题是示例中的失败测试。

申请XAML

<Application x:Class="InvokeTest.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:InvokeTest"
             Startup="Application_Startup"/>

申请CS

using System.Windows;

namespace InvokeTest
{
    public partial class App : Application
    {
        private void Application_Startup(object sender, StartupEventArgs e)
        {
            var view = new MainWindow();
            var viewmodel = new MainWindowViewModel();
            view.DataContext = viewmodel;
            view.Show();
        }
    }
}

Window XAML

<Window x:Class="InvokeTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:InvokeTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
<TextBox x:Name="MyTextBox" x:FieldModifier="public" Text="{Binding TextProperty, UpdateSourceTrigger=PropertyChanged}" />
</Window>

Window CS

using NUnit.Framework;
using System.Diagnostics;
using System.Threading;
using System.Windows;
using System.Windows.Automation.Peers;
using System.Windows.Automation.Provider;
using System.Windows.Controls;

namespace InvokeTest
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }

    public class MainWindowViewModel
    {
        string textfield;
        public string TextProperty
        {
            get { DebugLog("getter"); return textfield; }
            set { textfield = value; DebugLog("setter"); }
        }

        private void DebugLog(string function)
        {
            Debug.WriteLine(ToString() + " " + nameof(TextProperty) + " " + function + " was called. value: '" + textfield ?? "<null>" + "'");
        }

        [TestFixture, Apartment(ApartmentState.STA)]
        public class WPFTest
        {
            MainWindow view;
            MainWindowViewModel viewmodel;

            [SetUp]
            public void SetUp()
            {
                view = new MainWindow();
                viewmodel = new MainWindowViewModel();
                view.DataContext = viewmodel;
            }

            [Test]
            public void SetTextBox_NoAutomation()
            {
                string expected = "I want to set this";
                view.MyTextBox.Text = expected;
                Assert.AreEqual(expected, viewmodel.TextProperty);
                /*
                Test Name:  SetTextBox_NoAutomation
                Test Outcome:   Failed
                Result Message: 
                Expected: "I want to set this"
                But was:  null
                */
            }

            [Test]
            public void SetTextBox_UIAutomation()
            {
                string expected = "I want to set this";
                SetValue(view.MyTextBox, expected);
                Assert.AreEqual(expected, viewmodel.TextProperty);
                /*
                Test Name:  SetTextBox_UIAutomation
                Test Outcome:   Failed
                Result Message: 
                Expected: "I want to set this"
                But was:  null
                */
            }
            private static void SetValue(TextBox textbox, string value)
            {
                TextBoxAutomationPeer peer = new TextBoxAutomationPeer(textbox);
                IValueProvider provider = peer.GetPattern(PatternInterface.Value) as IValueProvider;
                provider.SetValue(value);
            }
        }
    }
}

编辑 #1:@Nkosi 指出我的 xaml
中存在绑定失败 编辑 #2:添加了一些锅炉以启用手动测试,还添加了一个用例来显示没有 uiautomation 的行为。那只是一个旁注,uiautomation是这个问题的核心。

其实你可以调用TextBox.Text Property

view.MyTextBox.Text = expected;

在您看来,您还绑定了视图模型上的 Text 属性,而测试中的视图模型具有 MyTextBox 属性。一个或另一个需要更新以匹配。

public class MainWindowViewModel
{
    public string Text { get; set; }
}

[TestFixture, Apartment(ApartmentState.STA)]
public class WPFTest
{
    [Test]
    public void SetTextBox()
    {
        //Arrange
        var expected = "I want to set this";

        var view = new MainWindow();
        var viewmodel = new MainWindowViewModel();
        view.DataContext = viewmodel;

        //Act
        view.MyTextBox.Text = expected;

        //Assert
        Assert.AreEqual(expected, viewmodel.Text);
    }
}

我需要显示 window。 我假设它是为了获取消息泵 运行.

如果有人可以提供详细信息,我会将该答案设置为已接受的答案。

using NUnit.Framework;
using System.Diagnostics;
using System.Threading;
using System.Windows;
using System.Windows.Automation.Peers;
using System.Windows.Automation.Provider;
using System.Windows.Controls;

namespace InvokeTest
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }

    public class MainWindowViewModel
    {
        string textfield;
        public string TextProperty
        {
            get { DebugLog("getter"); return textfield; }
            set { textfield = value; DebugLog("setter"); }
        }

        private void DebugLog(string function)
        {
            Debug.WriteLine(ToString() + " " + nameof(TextProperty) + " " + function + " was called. value: '" + textfield ?? "<null>" + "'");
        }

        [TestFixture, Apartment(ApartmentState.STA)]
        public class WPFTest
        {
            MainWindow view;
            MainWindowViewModel viewmodel;

            [SetUp]
            public void SetUp()
            {
                view = new MainWindow();
                viewmodel = new MainWindowViewModel();
                view.DataContext = viewmodel;
                view.Show();
            }

            [TearDown]
            public void TearDown()
            {
                view.Close();
                view.DataContext = null;
                view = null;
                viewmodel = null;
            }

            [Test]
            public void SetTextBox_NoAutomation()
            {
                string expected = "I want to set this";
                view.MyTextBox.Text = expected;
                Assert.AreEqual(expected, viewmodel.TextProperty);
            }

            [Test]
            public void SetTextBox_UIAutomation()
            {
                string expected = "I want to set this";
                SetValue(view.MyTextBox, expected);
                Assert.AreEqual(expected, viewmodel.TextProperty);
            }

            private void SetValue(TextBox textbox, string value)
            {
                TextBoxAutomationPeer peer = new TextBoxAutomationPeer(textbox);
                IValueProvider provider = peer.GetPattern(PatternInterface.Value) as IValueProvider;
                provider.SetValue(value);
            }
        }
    }
}