本文来安利大家一个支持使用中文做用例名的 WPF 界面 UI 单元测试框架

卖点

有没有觉得命名太难?有没有觉得单元测试的命名更难?没错,这是一个业界的大问题。很多团队都会因为单元测试的用例函数命名太难而让团队成员不喜欢写单元测试,或者说代码审查的时候觉得对方写的单元测试用例名有语法错误,又或者是改到单元测试时发现函数命名因为自己英文能力有限而看不懂

本文安利给大家的 dotnetCampus.UITest.WPF 单元测试框架将用来解决此问题。使用它,你可以用契约的方式来描述一个又一个的测试用例,这些测试用例将在单元测试运行结束后显示到单元测试控制台或 GUI 窗口中。全过程你完全不需要为任何单元测试方法进行命名——你关注的,是测试用例本身

现在,你的单元测试可以这样写了:

    [TestClass]
public class DemoTest
{
[UIContractTestCase]
public void TestAsyncLoad()
{
"等待窗口显示出来,可以成功进行异步等待,不会锁主线程".Test(async () =>
{
var mainWindow = new MainWindow();
var taskCompletionSource = new TaskCompletionSource();
mainWindow.Loaded += (sender, args) => taskCompletionSource.SetResult();
await mainWindow.Dispatcher.InvokeAsync(mainWindow.Show);
await taskCompletionSource.Task;
});
}
}

于是,运行单元测试将看到这样的结果视图:

本 UI 单元测试框架,仅仅提供的是让你可以使用 CUint(Chinese Unit Test) 风格编写 UI 测试代码,所有的放在 Test 内的代码将会在 UI 线程执行。本 UI 单元测试框架不提供面向测试的辅助类型的方法,例如模拟鼠标点击等功能,如需这些功能,还请使用第三方的库进行辅助

使用方法

此单元测试框架是基于 MIT 最友好开源协议,在 GitHub 上完全开源的,请看 https://github.com/dotnet-campus/CUnit/

此单元测试框架是 MSTest v2 的一个扩展,在使用时,你需要创建一个 MSTest 的单元测试项目,在此单元测试项目里面额外安装 dotnetCampus.UITest.WPF 库。对于在使用新 SDK 风格的 csproj 文件,可以编辑加入如下代码进行安装库

<PackageReference Include="dotnetCampus.UITest.WPF" Version="2.2.0" />

如果你的单元测试项目里面包含了 WPF 的 App.xaml 文件,为了修复构建单元测试时有多个入口 Main 函数问题,你需要额外加入以下代码用于修复此问题

  <ItemGroup>
<ApplicationDefinition Remove="App.xaml" />
</ItemGroup>
<ItemGroup>
<Page Include="App.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>

以上对 App.xaml 的修复非必须,只有你的单元测试项目里面包含了 App.xaml 才有此需求。此问题不是 dotnetCampus.UITest.WPF 库引入,而是通用的单元测试就存在的问题。对于大部分的 UI 单元测试项目来说,都不会也不应该包含 App.xaml 文件,除非这是针对 WPF 的 UI 类库的单元测试。对于应用本身的 UI 单元测试来说,都应该传入的是应用的 App 类

更改完成之后的 csproj 的内容大概如下

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
<OutputType>exe</OutputType>
<DisableWinExeOutputInference>true</DisableWinExeOutputInference>
<TargetFramework>net6.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
<IsPackable>false</IsPackable>
</PropertyGroup> <ItemGroup>
<ApplicationDefinition Remove="App.xaml" />
</ItemGroup>
<ItemGroup>
<Page Include="App.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup> <ItemGroup>
<PackageReference Include="Moq" Version="4.16.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.8" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.8" />
<PackageReference Include="coverlet.collector" Version="3.1.0" />
<PackageReference Include="dotnetCampus.UITest.WPF" Version="2.2.0" />
</ItemGroup> </Project>

在开始编写实际的 UI 单元测试之前,需要初始化 UI 测试引擎,这是因为 WPF 需要给定指定的 App 入口函数,用于寻找程序集资源,代码如下

    [TestClass]
public class FooTest
{
[AssemblyInitialize]
public static void InitializeApplication(TestContext testContext)
{
UITestManager.InitializeApplication(() => new App());
}
}

在 WPF 里面,有资源程序集等概念,通过以上代码初始化引擎即可自动完成设置。在一个公开的标记了 TestClassAttribute 特性的测试类型里面,存放一个静态的,标记了 AssemblyInitializeAttribute 特性的带有 TestContext 参数的方法,将会在开始单元测试之前被执行。在此函数里面,需要调用 UITestManager 初始化引擎,将自己测试的项目里的 WPF 应用入口的 App 类传入

接下来即可开始编写业务上的单元测试代码,如以下例子

    [TestClass]
public class FooTest
{
[AssemblyInitialize]
public static void InitializeApplication(TestContext testContext)
{
UITestManager.InitializeApplication(() => new App());
} [UIContractTestCase]
public void TestAsyncLoad()
{
"等待窗口显示出来,可以成功进行异步等待,不会锁主线程".Test(async () =>
{
var mainWindow = new MainWindow();
var taskCompletionSource = new TaskCompletionSource();
mainWindow.Loaded += (sender, args) => taskCompletionSource.SetResult();
await mainWindow.Dispatcher.InvokeAsync(mainWindow.Show);
await taskCompletionSource.Task;
});
} [UIContractTestCase]
public void TestMainWindow()
{
"打开 MainWindow 窗口,可以成功打开窗口".Test(() =>
{
Assert.AreEqual(Application.Current.Dispatcher, Dispatcher.CurrentDispatcher);
var mainWindow = new MainWindow();
bool isMainWindowLoaded = false;
mainWindow.Loaded += (sender, args) => isMainWindowLoaded = true;
mainWindow.Show();
Assert.AreEqual(true, isMainWindowLoaded);
}); "关闭 MainWindow 窗口,可以成功关闭窗口和收到窗口关闭事件".Test(() =>
{
var window = Application.Current.MainWindow;
Assert.AreEqual(true, window is MainWindow);
bool isMainWindowClosed = false;
Assert.IsNotNull(window);
window.Closed += (sender, args) => isMainWindowClosed = true;
window.Close();
Assert.AreEqual(true, isMainWindowClosed);
});
}
}

每个进入的函数都是在 UI 线程执行的,可以放心调用任何的 UI 资源

代码

本文所有代码放在githubgitee 欢迎访问

可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 5d83d18e3f369c36759e1b3d1b6afc1a1c3cac30

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git

获取代码之后,进入 dotnetCampus.UITest.WPF.Demo 文件夹

dotnetCampus.UITest.WPF 一个支持中文用例的界面单元测试框架的更多相关文章

  1. 使用Swing组件编写一个支持中文文本编辑程序ChineseTextEdit.java

      import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.io.*; public class C ...

  2. 使用react-native做一个简单的应用-04界面主框架

    欢迎界面搭建完毕,我们接下来需要做的就是搭建应用程序的主体框架啦.首先我们看一下首页的截图: 从图中看到,我将首页分为了三部分:用黑色矩形表示的头部,绿色表示的内容和红色表示的底部. 下面我们需要解决 ...

  3. netty系列之:轻轻松松搭个支持中文的服务器

    目录 简介 netty的HTTP支持 netty中使用HTTP的原理 100 (Continue) Status 为netty搭建HTTP服务器 总结 简介 之前讲了那么多关于netty的文章,都是讲 ...

  4. JS导出PDF插件(支持中文、图片使用路径)

    在WEB上想做一个导出PDF的功能,发现jsPDF比较多人推荐,遗憾的是不支持中文,最后找到pdfmake,很好地解决了此问题.它的效果可以先到http://pdfmake.org/playgroun ...

  5. QT 4.7支持中文(QT4.7)(中文)(makeqpf)

    QT 4.7支持中文(QT4.7)(中文)(makeqpf) 摘要: QT4.7.0在移植到开发板上的时候,中文支持是必不可少的,如何让QT支持中文,如何制作QT支持的字体文件,如何使QT UI编辑器 ...

  6. IOS Android支持中文与本地文件的读取写入

    转自http://www.xuanyusong.com/archives/1069 和http://www.benmutou.com/archives/2094 前几天有个朋友问我为什么在IOS平台中 ...

  7. 美化你的GRUB,全面支持中文(菜单、提示、帮助)适用7.04-9.04

    本文根据网络资料整理而成,在此鸣谢各位作者. 本方法适合 7.04-9.04版本,9.10使用了grub2,请看这里. http://forum.ubuntu.org.cn/viewtopic.php ...

  8. Unity3D研究院之IOS Android支持中文与本地文件的读取写

       前几天有个朋友问我为什么在IOS平台中可以正常的读写文件可是在Android平台中就无法正常的读写.当时因为在上班所以我没时间来帮他解决,晚上回家后我就拿起安卓手机真机调试很快就定位问题所在,原 ...

  9. PHP生成PDF完美支持中文,解决TCPDF乱码

    PHP生成PDF完美支持中文,解决TCPDF乱码 2011-09-26 09:04 418人阅读 评论(0) 收藏 举报 phpfontsheaderttfxhtml文档 PHP生成PDF完美支持中文 ...

  10. 使用CEF(二)— 基于VS2019编写一个简单CEF样例

    使用CEF(二)- 基于VS2019编写一个简单CEF样例 在这一节中,本人将会在Windows下使用VS2019创建一个空白的C++Windows Desktop Application项目,逐步进 ...

随机推荐

  1. 10.Java异常问题

    目录介绍 10.0.0.1 见过哪些运行时异常?异常处理机制知道哪些?从异常是否必须需要被处理的角度来看怎么分类? 10.0.0.2 运用Java异常处理机制?异常处理的原理?Java中检查异常和非检 ...

  2. 为什么延迟删除可以保证MYSQL 与redis的一致性?

    看过很多保持MYSQL 与redis保持一致性的文章都提到了延迟删除,其实脱离任何业务场景的设计都是不切实际的,所以我会本着一个通用的读写场景去分析为什么延迟删除大概率可以保证MYSQL与redis的 ...

  3. 灰狼优化算法(MOGWO)

    灰狼优化算法(MOGWO) 摘要 固定大小的外部档案用来保存帕累托优化解 在多目标搜索空间中,这个档案被用来定义狼群社会等级和捕猎行为 这个算法在10个多目标测试集进行测试,并与MOEA/D和MOPS ...

  4. KingbaseES V8R6集群运维案例之---sys_hba.conf限制客户端访问数据库

    KingbaseES V8R6集群运维案例之---sys_hba.conf限制客户端访问数据库 案例说明: 客户端认证是由一个配置文件(通常名为sys_hba.conf并被存放在数据库集簇目录中)控制 ...

  5. #树形dp#C 树上排列

    分析 设\(dp[x][i]\)表示以\(x\)为根的子树中\(x\)的排名为\(i\)的方案数, 然后枚举子节点转移即可,Talk is cheap,Show me the code 代码 #inc ...

  6. 未来已来,OpenHarmony 3.2 Release发布,迈入发展新阶段

      2023年4月9日,在社区开发者的期盼中,在春风送暖万物更新的季节里,我们迎来了OpenAtom OpenHarmony(以下简称"OpenHarmony")3.2 Relea ...

  7. 深入理解 SQL UNION 运算符及其应用场景

    SQL UNION运算符 SQL UNION运算符用于组合两个或多个SELECT语句的结果集. 每个UNION中的SELECT语句必须具有相同数量的列. 列的数据类型也必须相似. 每个SELECT语句 ...

  8. 灵活配置 Spring 集合:List、Set、Map、Properties 详解

    使用<property>标签的value属性配置原始数据类型和ref属性配置对象引用的方式来定义Bean配置文件.这两种情况都涉及将单一值传递给Bean 那么如果您想传递多个值,例如Jav ...

  9. 深入了解 Spring Cloud Config、Spring Cloud Gateway 与断路器模式

    Spring Microservices 是一个框架,它使用 Spring 框架更容易地构建和管理基于微服务的应用程序.微服务是一种架构风格,其中一个大型应用程序被构建为一组小型.独立可部署的服务.每 ...

  10. 【Kotlin】类和对象

    1 前言 ​ Kotlin 是面向对象编程语言,与 Java 语言类似,都有类.对象.属性.构造函数.成员函数,都有封装.继承.多态三大特性,不同点如下. Java 有静态(static)代码块,Ko ...