dotnetCampus.UITest.WPF 一个支持中文用例的界面单元测试框架
本文来安利大家一个支持使用中文做用例名的 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 资源
代码
可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 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 一个支持中文用例的界面单元测试框架的更多相关文章
- 使用Swing组件编写一个支持中文文本编辑程序ChineseTextEdit.java
import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.io.*; public class C ...
- 使用react-native做一个简单的应用-04界面主框架
欢迎界面搭建完毕,我们接下来需要做的就是搭建应用程序的主体框架啦.首先我们看一下首页的截图: 从图中看到,我将首页分为了三部分:用黑色矩形表示的头部,绿色表示的内容和红色表示的底部. 下面我们需要解决 ...
- netty系列之:轻轻松松搭个支持中文的服务器
目录 简介 netty的HTTP支持 netty中使用HTTP的原理 100 (Continue) Status 为netty搭建HTTP服务器 总结 简介 之前讲了那么多关于netty的文章,都是讲 ...
- JS导出PDF插件(支持中文、图片使用路径)
在WEB上想做一个导出PDF的功能,发现jsPDF比较多人推荐,遗憾的是不支持中文,最后找到pdfmake,很好地解决了此问题.它的效果可以先到http://pdfmake.org/playgroun ...
- QT 4.7支持中文(QT4.7)(中文)(makeqpf)
QT 4.7支持中文(QT4.7)(中文)(makeqpf) 摘要: QT4.7.0在移植到开发板上的时候,中文支持是必不可少的,如何让QT支持中文,如何制作QT支持的字体文件,如何使QT UI编辑器 ...
- IOS Android支持中文与本地文件的读取写入
转自http://www.xuanyusong.com/archives/1069 和http://www.benmutou.com/archives/2094 前几天有个朋友问我为什么在IOS平台中 ...
- 美化你的GRUB,全面支持中文(菜单、提示、帮助)适用7.04-9.04
本文根据网络资料整理而成,在此鸣谢各位作者. 本方法适合 7.04-9.04版本,9.10使用了grub2,请看这里. http://forum.ubuntu.org.cn/viewtopic.php ...
- Unity3D研究院之IOS Android支持中文与本地文件的读取写
前几天有个朋友问我为什么在IOS平台中可以正常的读写文件可是在Android平台中就无法正常的读写.当时因为在上班所以我没时间来帮他解决,晚上回家后我就拿起安卓手机真机调试很快就定位问题所在,原 ...
- PHP生成PDF完美支持中文,解决TCPDF乱码
PHP生成PDF完美支持中文,解决TCPDF乱码 2011-09-26 09:04 418人阅读 评论(0) 收藏 举报 phpfontsheaderttfxhtml文档 PHP生成PDF完美支持中文 ...
- 使用CEF(二)— 基于VS2019编写一个简单CEF样例
使用CEF(二)- 基于VS2019编写一个简单CEF样例 在这一节中,本人将会在Windows下使用VS2019创建一个空白的C++Windows Desktop Application项目,逐步进 ...
随机推荐
- 记录--极致舒适的Vue页面保活方案
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 为了让页面保活更加稳定,你们是怎么做的? 我用一行配置实现了 Vue页面保活是指在用户离开当前页面后,可以在返回时恢复上一次浏览页面的状态 ...
- 记录--Openlayers 高德腾讯、百度、天地图坐标相互转换
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 在地图开发过程中,坐标的转换是很常用的功能,国内的话一般西安80(EPSG:4610).北京54(EPSG:2433)转WGS84比较多, ...
- FFmpeg开发笔记(七)欧拉系统编译安装FFmpeg
FFmpeg支持Linux.macOS.Windows.Android等操作系统,其中Linux系列包括Ubuntu.Debian.Mint.CentOS.RHEL.Fedora等分支.FFmpeg官 ...
- vue初学核心基础02
8.v-bind补充 8.1v-bind绑定类名 v-bind指令给"任意标签"的"任意属性"绑定数据 对于大部分的属性而言我们只需要直接赋值即可, 例如:va ...
- debian12 linux 安装xfce4
1.安装显示服务器 sudo apt install xorg 2.安装xfce sudo apt install xfce4 xfce4-goodies 3.安装显示管理器 sudo apt ins ...
- 【已解决】mybatis注解@Param失效,无法获取到值(org.apache.ibatis.binding.BindingException: Parameter 'policy' not found. Available parameters are [arg1, arg0, param1, param2])
案发现场: 传递的参数是一个实体类 PolicyDictionary 此时我无法拿到数据: 解决思路一(不推荐) 删去@Param注解,使用mybatis默认的参数顺序: 不使用@param注解传递多 ...
- #莫比乌斯反演#BZOJ 2694 LCM
题目 多组询问求 \[\sum_{i=1}^n\sum_{j=1}^m{|\mu(\gcd(i,j))|*lcm(i,j)}\pmod {2^{30}} \] \(T\leq 10^4,n,m\leq ...
- #拓扑排序#洛谷 4645 [COCI2006-2007 Contest#3] BICIKLI
题目 这个地方有 \(n\) 个城镇,从 \(1\sim n\) 编号, 其中有 \(m\) 条单向道路连接它们. 比赛将在 \(1\) 号城镇开始并在 \(2\) 号城镇结束. 主办方想知道,一共有 ...
- 如何知道 window 的 load 事件已经触发
背景 为了使页面加载更快,常常将一些不重要的第三方脚本在页面完成加载后进行懒加载. // 做一些不影响业务的事情 window.addEventListener('load', () => { ...
- JDK 14的新特性:文本块Text Blocks
目录 举个例子 Indentation编排 Escaping转义 formatted格式化 总结 说起来,Text Blocks是在JDK13中以第一次预览版本引入的.现在在JDK14中是第二次预览版 ...