补充说明——针对《C#:从 0 到 1 创建基于 NUnit + FlaUI 的 WPF UI 自动化测试项目》

一、「创建 Test 项目」章节:环境与前置配置(新手必看)

1. 被测 WPF 项目的前置配置(必须做!)

要让 FlaUI 能定位到 WPF 控件,需给 WPF 界面的控件设置AutomationId(唯一标识,比控件 Name 更稳定):打开 WPF 项目的MainWindow.xaml,给登录相关控件添加AutomationProperties.AutomationId

XML 复制代码
<!-- WPF登录界面的XAML示例 -->
<Grid>
    <!-- 账号输入框 -->
    <TextBox x:Name="txtAccount" 
             AutomationProperties.AutomationId="Login_AccountInput"
             Width="200" Height="30" Margin="10"/>
    
    <!-- 密码输入框 -->
    <PasswordBox x:Name="txtPwd" 
                 AutomationProperties.AutomationId="Login_PwdInput"
                 Width="200" Height="30" Margin="10,50,10,0"/>
    
    <!-- 登录按钮 -->
    <Button x:Name="btnLogin" 
            AutomationProperties.AutomationId="Login_LoginBtn"
            Content="登录" Width="80" Height="30" Margin="10,90,10,0"
            Click="BtnLogin_Click"/>
    
    <!-- 主页面标题(登录成功后显示) -->
    <TextBlock x:Name="txtHomeTitle" 
               AutomationProperties.AutomationId="HomePage_Title"
               Text="主页面" Visibility="Collapsed" Margin="10,130,10,0"/>
</Grid>
2. 测试项目的 NuGet 包安装步骤

打开 Visual Studio → 右键测试项目 → 选择「管理 NuGet 程序包」:

  • 搜索NUnit → 安装 3.13.3+版本(稳定版)
  • 搜索NUnit3TestAdapter → 安装(用于在 VS 中运行 NUnit 测试)
  • 搜索FlaUI.Core → 安装最新稳定版(如 4.0.0+)
  • 搜索FlaUI.UIA3 → 安装(WPF 应用优先用 UIA3 框架,兼容性更强)
3. 配置nunit.runsettings文件(统一测试环境)
  1. 在测试项目根目录右键 → 「添加」→「新建项」→ 选「XML 文件」,命名为nunit.runsettings
  2. 粘贴以下配置(指定测试报告路径、框架版本):
XML 复制代码
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
  <!-- 被测程序路径(可根据实际修改) -->
  <TestRunParameters>
    <Parameter name="TestAppPath" value="..\..\..\YourWpfApp\bin\Debug\net6.0-windows\YourWpfApp.exe" />
  </TestRunParameters>
  
  <!-- NUnit配置 -->
  <NUnit>
    <NumberOfTestWorkers>1</NumberOfTestWorkers> <!-- UI自动化建议单线程 -->
    <OutputXml>TestResults\NUnit_Report.xml</OutputXml> <!-- 原生XML报告路径 -->
  </NUnit>
  
  <!-- .NET框架配置 -->
  <RunnerConfiguration>
    <TargetFrameworkVersion>net6.0</TargetFrameworkVersion> <!-- 与被测项目一致 -->
  </RunnerConfiguration>
</RunSettings>
  1. 应用配置:VS 顶部「测试」→「测试设置」→「选择测试设置文件」→ 选中刚创建的nunit.runsettings
二、「用户登录」章节:完整测试代码 + 细节处理
1. 测试类的基础结构(含初始化 / 清理)
cs 复制代码
using NUnit.Framework;
using FlaUI.Core;
using FlaUI.Core.AutomationElements;
using FlaUI.UIA3;
using System;
using System.IO;

namespace WpfUiAutoTest
{
    // NUnit测试类
    [TestFixture]
    public class LoginTests
    {
        // 核心对象:自动化框架、被测应用、主窗口
        private UIA3Automation _automation;
        private Application _app;
        private Window _mainWindow;
        // 被测程序路径(从runsettings中读取)
        private string _testAppPath;


        // 【全局初始化】所有测试前执行1次
        [OneTimeSetUp]
        public void GlobalSetup()
        {
            // 从runsettings读取被测程序路径
            _testAppPath = TestContext.Parameters["TestAppPath"];
            // 验证路径是否存在
            if (!File.Exists(_testAppPath))
            {
                throw new FileNotFoundException("被测WPF程序不存在,请检查路径", _testAppPath);
            }
            // 初始化FlaUI自动化框架
            _automation = new UIA3Automation();
        }


        // 【每个测试前初始化】每个测试用例执行前启动应用
        [SetUp]
        public void TestSetup()
        {
            // 启动被测WPF程序
            _app = Application.Launch(_testAppPath);
            // 等待应用启动(最多等10秒)
            _app.WaitUntilMainWindowIsLoaded(TimeSpan.FromSeconds(10));
            // 获取主窗口
            _mainWindow = _app.GetMainWindow(_automation);
            // 最大化窗口(避免元素位置偏移)
            _mainWindow.Maximize();
        }


        // 【用户登录测试用例】
        [Test]
        [Description("验证正确账号密码能否成功登录")]
        public void Login_WithValidAccount_Success()
        {
            // --------------------------
            // 步骤1:定位登录界面元素
            // --------------------------
            // 账号输入框(等待元素加载,最多5秒)
            var accountInput = _mainWindow
                .WaitUntilExists(cf => cf.ByAutomationId("Login_AccountInput"), TimeSpan.FromSeconds(5))
                ?.AsTextBox();
            Assert.IsNotNull(accountInput, "账号输入框未找到");

            // 密码输入框
            var pwdInput = _mainWindow
                .WaitUntilExists(cf => cf.ByAutomationId("Login_PwdInput"), TimeSpan.FromSeconds(5))
                ?.AsPasswordBox(); // 注意:PasswordBox需用AsPasswordBox()
            Assert.IsNotNull(pwdInput, "密码输入框未找到");

            // 登录按钮(等待按钮可点击)
            var loginBtn = _mainWindow
                .WaitUntilExists(cf => cf.ByAutomationId("Login_LoginBtn"), TimeSpan.FromSeconds(5))
                ?.AsButton();
            Assert.IsNotNull(loginBtn, "登录按钮未找到");
            Assert.IsTrue(loginBtn.IsEnabled, "登录按钮不可点击");


            // --------------------------
            // 步骤2:模拟用户操作
            // --------------------------
            // 输入账号(清空原有内容)
            accountInput.Text = string.Empty;
            accountInput.Enter("test_user");

            // 输入密码(PasswordBox需用SecurePassword,或直接Enter)
            pwdInput.Password = string.Empty;
            pwdInput.Enter("test_pwd_123");

            // 点击登录按钮
            loginBtn.Click();


            // --------------------------
            // 步骤3:验证登录结果
            // --------------------------
            // 等待主页面标题显示(最多等5秒)
            var homeTitle = _mainWindow
                .WaitUntilExists(cf => cf.ByAutomationId("HomePage_Title"), TimeSpan.FromSeconds(5))
                ?.AsTextBlock();
            Assert.IsNotNull(homeTitle, "登录后未进入主页面");
            Assert.AreEqual("主页面", homeTitle.Text, "主页面标题显示错误");
        }


        // 【每个测试后清理】关闭应用,释放资源
        [TearDown]
        public void TestTearDown()
        {
            // 关闭应用(优先优雅关闭,失败则强制终止)
            if (_app != null)
            {
                try
                {
                    _app.Close();
                    // 等待进程退出(最多5秒)
                    if (!_app.WaitForExit(TimeSpan.FromSeconds(5)))
                    {
                        _app.Kill();
                    }
                }
                catch (Exception ex)
                {
                    TestContext.WriteLine($"关闭应用失败:{ex.Message}");
                    _app?.Kill();
                }
            }
            // 释放对象
            _mainWindow?.Dispose();
            _app?.Dispose();
        }


        // 【全局清理】所有测试后释放框架
        [OneTimeTearDown]
        public void GlobalTearDown()
        {
            _automation?.Dispose();
        }
    }
}
2. 常见问题处理(新手易踩坑)
  • 密码框输入失败 :WPF 的PasswordBox不能用AsTextBox(),必须用AsPasswordBox(),且优先用Enter()方法输入(模拟真实键盘输入)。
  • 元素定位不稳定 :避免用By.Name()定位(Name 可能随语言切换变化),优先用ByAutomationId();如果控件没有AutomationId,可结合By.ControlType()+By.Index()(但稳定性较差)。
  • 应用启动超时 :如果 WPF 程序启动慢,可延长WaitUntilMainWindowIsLoaded的超时时间(如 15 秒)。
三、「导出测试文件」章节:ExtentReports 可视化报告(带截图)
1. 安装 ExtentReports 包

在测试项目的 NuGet 中搜索ExtentReports,安装 4.1.0+版本(兼容 NUnit3)。

2. 集成 ExtentReports 到测试类
cs 复制代码
// 新增引用
using AventStack.ExtentReports;
using AventStack.ExtentReports.Reporter;
using FlaUI.Core.Capturing;

// 在LoginTests类中新增以下成员
private static ExtentReports _extentReport;
private ExtentTest _currentTest;


// 【全局初始化】添加报告初始化
[OneTimeSetUp]
public void GlobalSetup()
{
    // 原有逻辑...

    // 初始化ExtentReports
    var reportDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestResults");
    Directory.CreateDirectory(reportDir); // 确保目录存在
    var htmlReporter = new ExtentHtmlReporter(Path.Combine(reportDir, "Extent_Report.html"));
    
    // 配置报告样式(可选)
    htmlReporter.Config.DocumentTitle = "WPF UI自动化测试报告";
    htmlReporter.Config.ReportName = "用户登录模块测试";
    htmlReporter.Config.Theme = Theme.Dark; // 深色主题

    _extentReport = new ExtentReports();
    _extentReport.AttachReporter(htmlReporter);
}


// 【每个测试前】创建测试报告节点
[SetUp]
public void TestSetup()
{
    // 原有逻辑...

    // 创建当前测试的报告节点
    _currentTest = _extentReport.CreateTest(TestContext.CurrentContext.Test.Name, 
                                            TestContext.CurrentContext.Test.Properties.Get("Description")?.ToString());
}


// 【用户登录测试用例】新增报告日志+截图
[Test]
[Description("验证正确账号密码能否成功登录")]
public void Login_WithValidAccount_Success()
{
    try
    {
        _currentTest.Log(Status.Info, "开始执行【正确账号登录】测试用例");

        // 步骤1:定位元素(新增日志)
        _currentTest.Log(Status.Info, "定位账号输入框");
        var accountInput = _mainWindow.WaitUntilExists(cf => cf.ByAutomationId("Login_AccountInput"), TimeSpan.FromSeconds(5))?.AsTextBox();
        Assert.IsNotNull(accountInput, "账号输入框未找到");
        _currentTest.Log(Status.Pass, "账号输入框定位成功");


        // 步骤2:模拟输入(新增日志)
        _currentTest.Log(Status.Info, "输入账号:test_user");
        accountInput.Text = string.Empty;
        accountInput.Enter("test_user");


        // 步骤3:点击登录后,添加界面截图
        loginBtn.Click();
        _currentTest.Log(Status.Info, "点击登录按钮");
        
        // 捕获当前界面截图
        var screenshot = Capture.Screenshot(_mainWindow);
        var screenshotPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestResults", $"{TestContext.CurrentContext.Test.Name}_after_login.png");
        screenshot.Save(screenshotPath);
        // 将截图添加到报告
        _currentTest.AddScreenCaptureFromPath(screenshotPath);


        // 步骤4:验证结果(新增日志)
        _currentTest.Log(Status.Info, "验证是否进入主页面");
        var homeTitle = _mainWindow.WaitUntilExists(cf => cf.ByAutomationId("HomePage_Title"), TimeSpan.FromSeconds(5))?.AsTextBlock();
        Assert.IsNotNull(homeTitle, "登录后未进入主页面");
        _currentTest.Log(Status.Pass, "登录成功,主页面显示正常");
    }
    catch (Exception ex)
    {
        // 测试失败时,捕获截图并记录日志
        var errorScreenshot = Capture.Screenshot(_mainWindow);
        var errorScreenshotPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestResults", $"{TestContext.CurrentContext.Test.Name}_error.png");
        errorScreenshot.Save(errorScreenshotPath);
        _currentTest.AddScreenCaptureFromPath(errorScreenshotPath);
        
        _currentTest.Log(Status.Fail, $"测试失败:{ex.Message}");
        throw; // 抛出异常,让NUnit标记测试失败
    }
}


// 【全局清理】生成报告
[OneTimeTearDown]
public void GlobalTearDown()
{
    // 原有逻辑...

    // 生成Extent报告
    _extentReport.Flush();
}
3. 查看测试报告

测试运行完成后:

  1. 打开测试项目的TestResults文件夹
  2. 双击Extent_Report.html → 用浏览器打开(可看到带截图、步骤日志的可视化报告)
四、通用调试工具:FlaUInspect 的使用

FlaUInspect 是 FlaUI 官方的控件查看工具,能帮你快速获取控件的AutomationId

  1. 下载:在 GitHub 搜索FlaUInspect → 下载最新版本的 exe
  2. 使用步骤:
    • 打开被测 WPF 程序
    • 打开 FlaUInspect → 点击左上角「Select Application」→ 选择你的 WPF 程序进程
    • 点击 FlaUInspect 的「Pick」按钮 → 鼠标移到 WPF 界面的控件上(如账号输入框)
    • 在 FlaUInspect 的「Properties」面板中,找到AutomationId(直接复制用于测试代码)
相关推荐
Scout-leaf5 小时前
WPF新手村教程(三)—— 路由事件
c#·wpf
用户298698530148 小时前
程序员效率工具:Spire.Doc如何助你一键搞定Word表格排版
后端·c#·.net
mudtools1 天前
搭建一套.net下能落地的飞书考勤系统
后端·c#·.net
玩泥巴的2 天前
搭建一套.net下能落地的飞书考勤系统
c#·.net·二次开发·飞书
唐宋元明清21882 天前
.NET 本地Db数据库-技术方案选型
windows·c#
lindexi2 天前
dotnet DirectX 通过可等待交换链降低输入渲染延迟
c#·directx·d2d·direct2d·vortice
修炼前端秘籍的小帅2 天前
Stitch——Google热门的免费AI UI设计工具
前端·人工智能·ui
王码码20352 天前
Flutter for OpenHarmony:socket_io_client 实时通信的事实标准(Node.js 后端的最佳拍档) 深度解析与鸿蒙适配指南
android·flutter·ui·华为·node.js·harmonyos
qq_454245032 天前
基于组件与行为的树状节点系统
数据结构·c#
bugcome_com2 天前
C# 类的基础与进阶概念详解
c#