使用带有MVC / MVP架构的 win 表单,我通常会使用一个类来包装视图以测试 UI,同时为模型和控制器/演示器使用模拟。包装类将通过属性和事件使 UI 中的大多数内容成为测试运行器的可观察属性。
这是测试 WPF 应用程序的可行方法吗?有没有更好的办法?有什么需要注意的问题吗?
至于测试本身,您可能最好使用UI 自动化框架。或者,如果您想要更流畅且独立于 wpf/winforms/win32/swt 的框架使用方式,您可以从 Codeplex 下载White(前提是您能够在您的环境中使用开源代码)。
对于陷阱;如果您尝试测试您的视图,您可能会遇到一些线程问题。例如,如果您正在运行 NUnit,默认的 testrunner 将在 MTA(多线程 Appartment)中运行,而 WPF 需要作为 STA(单线程 Appartment)运行。Mike Two在单元测试 WPF 方面非常容易上手,但没有考虑线程问题。Josh Smith 在这篇文章中对线程问题有一些想法,他还指出了 Chris Hedgate 的这篇文章。Chris 使用 Peter Provost 的CrossThreadTestRunner的修改版本以更友好的方式包装 MTA/STA 问题。
2016 更新:使用免费的 TestStack.White 框架来自动化 WPF UI 测试
将启动 WPF 应用程序、单击按钮并检查结果的示例如下所示:
using TestStack.White;
using TestStack.White.UIItems;
using TestStack.White.Factory;
[TestMethod]
public void TestDoSomething()
{
//Opens the app
var app = Application.Launch("MyApp.exe");
//Finds the main window (this and above line should be in [TestInitialize])
var window = app.GetWindow("My App Window Title", InitializeOption.NoCache);
//Finds the button (see other Get...() methods for options)
var btnMyButton = window.Get<Button>("btnMyButtonWPFname");
//Simulate clicking
btnMyButton.Click();
//Gets the result text box
//Note: TextBox/Button is in TestStack.White.UIItems namespace
var txtMyTextBox = window.Get<TextBox>("txtMyTextBox");
//Check for the result
Assert.IsTrue(txtMyTextBox.Text == "my expected result");
//Close the main window and the app (preferably in [TestCleanup])
app.Close();
}
@马特大卫,
请阅读文档并查看 Microsoft CompositeWPF(又名 Prism)的代码示例。这是一个专门为教授如何以测试驱动的方式处理 MVP/MVC 架构而创建的项目。他们的示例应用程序包含针对演示者\控制器的单元测试和非常酷的 UI 验收测试(他们使用White 框架来模拟用户操作)
手动。如果这就是你所得到的,我不是自动化UI 测试的忠实拥护者。我不确定 WPF 指南(需要通过 aku 的链接阅读).. 因为可以说它们仍在巩固...... WPF 从“什么是正确的方式”的角度来看并没有稳定下来。除非您使用这些不断发展的框架之一..我会保守地努力
PS:你可能想看这个(Mary Poppendieck 的 Google Talk on Lean).. 尤其是关于在测试中自动化的部分
由于 Coded UI 框架在 Visual Studio 版本 2019 ( Deprecated Coded UI ) 之后过期,Microsoft 建议使用带有 WinAppDriver 的 Appium 来测试 Windows 应用程序(桌面和 UWP)。您可以直接使用 Appium(带有 WinAppDriver)或 WinAppDriver 来运行测试(带有或不带有 Appium 的 WinAppDriver)。
WinAppDriver 直接
以下是直接使用 WinAppDriver 的简短描述:
下载并安装 WinAppDriver:
在 Windows 设置中启用开发者模式
启动 WinAppDriver:
C:\Program Files (x86)\Windows 应用程序驱动程序\WinAppDriver.exe
创建一个新的 Visual Studio 2019 单元测试项目 (.NET Framework)
添加 NuGet 包:Appium.WebDriver Microsoft.WinAppDriver.Appium.WebDriver(来自 Microsoft 的评论:建议使用 WinAppDriver NuGet 包来充分利用 Actions API 的高级输入。)
添加一个新类 DesktopSession:
public class DesktopSession
{
protected const string WindowsApplicationDriverUrl = "http://127.0.0.1:4723";
private const string NotepadAppId = @"C:\Windows\System32\notepad.exe";
protected static WindowsDriver<WindowsElement> session;
protected static WindowsElement editBox;
public static void Setup(TestContext context)
{
// Launch a new instance of Notepad application
if (session == null)
{
// Create a new session to launch Notepad application
var appCapabilities = new DesiredCapabilities();
appCapabilities.SetCapability("app", NotepadAppId);
appCapabilities.SetCapability("platformName", "Windows");
appCapabilities.SetCapability("deviceName ", "WindowsPC");
session = new WindowsDriver<WindowsElement>(new Uri(WindowsApplicationDriverUrl), appCapabilities);
Assert.IsNotNull(session);
Assert.IsNotNull(session.SessionId);
// Set implicit timeout to 1.5 seconds to make element search to retry every 500 ms for at most three times
session.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(1.5);
// Keep track of the edit box to be used throughout the session
editBox = session.FindElementByClassName("Edit");
Assert.IsNotNull(editBox);
}
}
public static void TearDown()
{
// Close the application and delete the session
if (session != null)
{
session.Close();
try
{
// Dismiss Save dialog if it is blocking the exit
session.FindElementByName("Nicht speichern").Click();
}
catch { }
session.Quit();
session = null;
}
}
[TestInitialize]
public void TestInitialize()
{
// Select all text and delete to clear the edit box
editBox.SendKeys(Keys.Control + "a" + Keys.Control);
editBox.SendKeys(Keys.Delete);
Assert.AreEqual(string.Empty, editBox.Text);
}
}
[TestClass]
public class UnitTest1 : DesktopSession
{
[TestMethod]
public void EditorEnterText()
{
Thread.Sleep(TimeSpan.FromSeconds(2));
editBox.SendKeys("abcdeABCDE 12345");
Assert.AreEqual(@"abcdeABCDE 12345", editBox.Text);
}
[ClassInitialize]
public static void ClassInitialize(TestContext context)
{
Setup(context);
}
[ClassCleanup]
public static void ClassCleanup()
{
TearDown();
}
}
(示例代码主要复制自WinAppDriver .NotepadTest)。
Appium 与 WinAppDriver
如果你想使用 Appium 运行测试,那么你必须在你的机器上安装正确版本的 WinAppDriver。Appium 的安装程序还应该在您的机器上安装正确版本的 WinAppDriver(请为所有用户安装 Appium)。就我而言,不幸的是,这不起作用。所以我看一下文件:
C:\Program Files\Appium\resources\app\node_modules\appium\node_modules\appium-windows-driver\lib\installer.js
在这里您将找到正确的版本和下载路径:
const WAD_VER = "1.1";
const WAD_DL = `https://github.com/Microsoft/WinAppDriver/releases/download/v${WAD_VER}/WindowsApplicationDriver.msi`;
如果你安装了正确的 WinAppDriver,你就可以启动 Appium。
重要提示:您必须更改 ApplicationDriverUrl
protected const string WindowsApplicationDriverUrl = "http://127.0.0.1:4723/wd/hub";
工具:
WindowsAppDriver 和 UI REcorder 发布 或下载WinAppDriver 存储库并在子目录 tools\UIRecorder 中构建 WinAppDriverUIRecorder.sln
其他链接: WinAppDriver FAQ Appium
这个问题仍然是相关的,但许多答案已经过时了。@deadpikle在评论 中提出了一个很好的解决方案,我试过了,我想把它作为答案,以便更多人看到。
所以,这里有一个库https://github.com/FlaUI/FlaUI。这是 WPF 应用程序的快速入门指南:
从 nuget 安装 FlaUI.UIA3
写这个来测试应用程序是否正确运行(但插入你的字符串文字):
using FlaUI.Core;
using FlaUI.Core.AutomationElements;
using FlaUI.UIA3;
using FluentAssertions;
using System;
using Xunit;
namespace Functional
{
public sealed class General : IDisposable
{
private readonly Application _app = Application.Launch(@"..\App.exe");
[Fact]
public void AppStarts()
{
using var automation = new UIA3Automation();
Window window = _app.GetMainWindow(automation, TimeSpan.FromSeconds(3));
window.Should().NotBeNull("null means the window failed to load");
window.Title.Should().Be("App title",
"otherwise, it could be message box with error in case of the wrong configuration");
}
public void Dispose()
{
_app.Close();
_app.Dispose();
}
}
}
此代码在 GitHub Actions 管道中也能很好地工作。
一定要看看 TestAutomationFX.com。一个人可以投入(好吧,我做过)很多时间来尝试捕捉/记录与 White 的事件。(在我的任务开始时,我忽略了其他地方提到的一两个帖子)。
我当然第二点关于最好的测试类型不是 UI 测试。
但是,如果有人打算在 UI 中做一些可自动化的事情来解决其他类型测试覆盖的缺点,那么 TAFX 似乎是那里最快的途径。
尝试使用 Ranorex V2.0 实现 WPF 自动化。使用 RanoreXPath 和 Ranorex 存储库,测试自动化代码可以与识别信息完全分离。Ranorex 还提供了基于 RanoreXPath 对象的捕获/回放编辑器。
我会推荐 TestAutomationFX 以及简单的 ui 测试自动化。TestAutomationFX 允许您使用 wpf 的 netAdvantage 工具,它不适用于白色和 QTP。TestAutomationFX 具有易于使用的界面,它与 Visual Studio 集成,并具有用于记录用户事件的良好记录器。