目录
前言
xUnit和MSTest同属于单元测试框架,而Specflow属于BDD (行为驱动开发)框架。
其实三者的应用都非常简单,但是如果你从没有接触过具体的示例,从零开始可能会一头雾水。
我之前写过一个实验性质的分布式事件总线,用到了以上三种框架,想学习.NET测试技术的同学可以直接到 MaH.EventBus - Repos (azure.com)查看示例。
另外推荐两个做.NET单元测试几乎必用的类库:NSubstitute,Shouldly. 二者可以很方便的帮我们Mock接口或数据,以及做断言等。
单元测试
刷过Leetcode的同学都知道,当我们提交完答案之后,系统会告诉我们是否正确;如果错误的话,会有导致错误的数据示例,其实它的背后就是单元测试在起作用。
单元测试就是:给定输入,判断输出是否满足预期
为特定的算法(无论业务算法或数理算法)编写高覆盖率的单元测试是非常有用的,它可以在开发过程中发现问题,也可以为日后的代码重构提供保证。
人们常常忽视单元测试的作用,其实单元测试的价值也是满足二八定律的,80%的测试用例可能就像是摆设一样,写完之后永远也不需要再维护。而15%的测试用例需要随着业务代码的变更进行修改,大概只有5%的单元测试能够在代码重构时真正的发挥作用。
当然,天下没有空中楼阁,你不可能建一个只有二楼没有一楼的房子,你也不能只写那“5%”的测试用例。VS或者SonarQube都可以为我们提供单元测试覆盖率分析,一般超过80%覆盖率被认为是健康状态。
不是所有的代码都能编写单元测试
单元测试的对象最好是纯函数,比如 int Add(int a, int b) => a + b; 这种结果完全由输入决定的函数。在VS2022中,对于未访问到任何实例变量的函数,VS会提示你将它修改为static,这种函数就属于纯函数,编写单元测试也会非常容易。
但是对于业务项目来讲,非纯函数是无法避免的,例如,任何包含I/O操作的方法都是非纯函数。
常见的I/O操作包含数据库连接,网络请求,以及Task.Delay,DateTime.Now等。我们应尽可能的将非纯方法封装在一个最小范围内。
另外依赖具体实例而非接口的方法也是非纯方法。
单元测试要保证纯洁性,意味着无论什么时候执行结果都必须是一致的,因此我们常常需要Mock一些数据接口等。
假如你的代码中直接使用DateTime.Now来做逻辑判断,那么你必须将当前代码重构,把获取当前时间的方法包装起来,通过接口提供给具体的业务逻辑使用,不然是没办法对其编写单元测试。
EF 单元测试
假如你项目中使用的是EF的话,那么写起单元测试来会非常简单,你可以直接使用Sqlite或者InMemory来创建一个基于内存的数据库,这样就可以很轻松的使用真实的DbContext来编写测试用例了:
private static DemoDbContext CreateInMenoryDatabase()
{
var connection = new SqliteConnection("Filename=:memory:");
connection.Open();
var contextOptions = new DbContextOptionsBuilder<DemoDbContext>()
.UseSqlite(connection)
.EnableSensitiveDataLogging()
.Options;
var context = new DemoDbContext(contextOptions);
context.Database.EnsureCreated();
return context;
}
xUnit和MSTest
这两个种项目类型都可以在VS中找到并且直接创建,创建完成之后我们可以在测试资源管理器找到创建好的项目以及测试用例。
假如你在使用VS企业版的话,还可以使用Live Unit Testing的功能, 它会在你编写的时候实时编译并运行单元测试,并且会在代码行的左侧用蓝色或者红色对勾表示当前行是否被覆盖,以及相应的测试用例成功与否。
你还可以生成并导出包含分支,代码行覆盖率等信息的测试分析报告等。
xUnit和MSTest使用上最大的区别在于测试用例方法的特性不同,这里不再赘述,感兴趣的朋友可以到前言中提到的代码仓库查看。
Specflow
Specflow旨在用自然语言编写测试用例,让非技术人员也能看得懂。但其背后仍然需要技术实现,并且与单元测试比起来,往往测试流程与实现也更为复杂。
创建Specflow项目需要先到VS扩展当中安装Specflow插件
先给大家看下Specflow Test的代码样式,推荐大家在创建Specflow Test的时候按照我的项目格式来做,在编写Scenario,Steps时会很方便。
Specflow并非只适用于测试流程,比如我之前博客中提到的基于Camunda BPMN创建的项目,以及包含任何流程的业务场景,任何场景都可以使用它来做测试。
我上面这个Demo仓库对Specflow的使用比较简单,只是做了个LocalEventBus的使用场景,给出了一个Specflow项目应有的结构。
大家可以自己自行学习下其他常用关键词的用法:
Given、When、Then、And、Background、Examples...
以及传参时会用到的占位符:
'(.*)'
还有Table类型的参数以及解析方法的使用:
[Given(@"Hello '(.*)'")]
public void Hello(string name, Table table)
{
var rows = table.CreateSet<T>();
}
我这里只做展示,不做具体讲解。