系列目录
为了支持跨平台, 微软为. net 平台提供了. net core test sdk, 这样第三方测试框架诸如 Nunit,Xunit 等只需要按照 sdk 提供的 API 规范进行开发便可以被 dotnet cli 工具调用, 这样就解决了在持续集成过程中第三方框架依赖于 Windows 平台上的各自 runner 的问题, 使得测试框架开发者不需要花费很大功夫就可以快速迁移到. net core 平台, 同时封装了各测试框架的实现细节, 对外暴露统一调用接口, 大大减少 devops 开发者的工作量.
作为单元测试基础知识介绍, 这里只介绍常用单元测试框架 Nunit 和 Xunit 如何在. net core 平台上使用, 并介绍由于. net core 的变化所引起的需要注意的测试代码的相应改变, 对于如何在 Jenkins 环境中自动完成测试的相关内容 Jenkins 基础知识里面介绍
考虑到实际工作中可能有的项目组已经使用或者尝试使用 xunit, 并且. net core 对 xunit 支持较好, 诸多开源项目的单元测试也都使用的是 Xunit, 本章节作为补充对 Xunit 基础知识进行讲解, 以帮助还不太了解这个框架的同学快速入门.
我们知道在. net framework 下相要对 mvc 项目进行集成测试非常困难, 一方面. net http 管道里有很多黑盒子, 开发者对它的实现细节一无所知, 二者需要 mock 的对象太多, 工作量巨大. 因此很难在持续集成环境中对 web 项目进行集成测试. 开发者或者测试人员大都依赖 postman,fiddler, 以及浏览器的 http 请求插件来进行集成测试, 这样带来的一个很大问题这些 http 请求很难复用, 更难以有效的组织管理. 即使使用 postman 这样强大的 http 工具如果测试接口过多代码也会变得一塌糊涂, 过一段时间后想要知道哪个方法是测试哪个接口用的就需要通过搜索来导航到指定的测试方法, 并且很多时候有于各模块有相同名称的方法, 往往需要先找到方法所在的 area, 然后再找到 controller 然后再找到相应方法... 如果出现问题的代码过多往往把开发者搞的焦头烂额, 苦不堪言.
幸运的是在. net core 里很容易模拟一个 httpt 管道, 这一方面使得集成测试在持续集成环境中使用提供了可能, 另一方 http 请求写成程序里, 可以很方便的导航到指定的测试方法, 极大提供可维护性. 本章节最后会介绍如何搭建一个. net core Web 项目的 selfhost 环境以供在单元测试框架中使用.
下面我们将简要介绍如何在 vs 中配置 xunt 环境以及 Xunit 断言的基本使用
.net core 中使用 Xunit
Xunit 是. net 平台下的一款单元测试工具, 类似 Nunit. 但是更为轻量, 更加专注于单元测试而不像 Nunit 提供了很多额外的功能
.net core 对 Xunit 支持较好, VisualStudio 2017 提供有一个 Xunit 单元测试模板可以很方便的创建一个 Xunit 单元测试项目.
如图, 在 visual studio 里创建项目时, 选择. net core 项目, 然后从模板里面找到 Xunit 单元测试项目便可以创建一个 Xunit 单元测试项目了.
我们打开刚创建的项目右键选择 "Nuget 包管理", 从包管理工具里我们可以看到, 实际上这个模板引用了 xunit, 和 xunit.runner.visualstudio 这两个包. 这样, 我们也可以自己手动创建一个. net core 类型的库文件, 然后引入这两个包, 能达到同样的效果.
这里建议大家通过模板来创建单元测试项目, 因为单元测试框架不同版本可能需要引用不同的包, 没有经验的同学常常由于引用的包不对导致单元测试项目跑不起来, 搞得灰头土脸, 非常郁闷.
我们编写以下单元测试代码
- public class UnitTest1
- {
- [Fact]
- public void Test1()
- {
- int intt = 3 + 2;
- Assert.True(intt==5);
- }
- }
通过以上代码我们看到 Xunit 就测试断言和 Nunit 很类似(这里指 Nunit3, 早期版本 Nunit 差异较大, 建议大这在工作中也尽量选用 Nunit3, 而不是 1 或者 2)
这里有一点差异需要指出, Xunit 并不需要对单元测试类进行注解(Nunit 是需要的, 否则无法识别), 只需要在需要测试的方法上加上 fact 注解即可.
单元测试方法的运行也和前面讲的 Nunit 单元测试运行方法相同, 这里不再赘述.
常见基本断言
虽然 Xunit 和 Nunit 在断言上有很多相似的地方, 并且有越来越像的趋势, 但是仍然有不少差别, 因此这里仍然会对 Xunit 的断言功能进行一个全面的列举, 以供大家速查. 并且有时候会指出它和 Nunit 的差别或者指出 Nunit 中比较难以实现或者技巧性很强的功能如何在 Xunit 里实现. 如果有读者直接阅读本章节而没有了解过 Nunit, 可以有选择的略过二者比较的内容.
这里首先指出一个很大的差别. Xunit 里并没有像 Nunit 里的 stringAssertion,FileAssertion 和 CollectionAssertion, 而是所有的断言都在 Assert 静态类里, 方法也不是很多, 语义也相对更加明确, 很适应没有单元测试基础的同学快速入门.
下面开始介绍 Xunit 里的断言方法
Assert.Null
用于断言一个对象是否是 Null
这个方法有一个相对含义的断言就是 Assert.NotNull, 很多其它的方法也有带 Not 的断言, 很容易理解.
Assert. Assert.Equal
此方法有很多重载, 用于比较两个字符串, int,decimal 或者对象类型是否相等.
注意这里比较两个对象是否相等时, 相当于 Object.equals()来比较两个对象是否相等
这个方法用于比较两个 double 类型值是否相等时, 可以指定精度.
Assert.StrictEqual
从字面上来看, 它用于比较两个对象是否是严格相等, 然而它的表现行为和以上 Equal 方法非常类似, 并不是比较两个对象内存地址是否相等. 实际上它是在比较的时候指定一个默认的比较器. 这个方法着实非常让人困惑.
Assert.same
用于比较两个对象运行是是否指定同一块内存地址, 如果要比较两个对象是否完全相等, 则使用它.
注意, 虽然 Assert.same 接收的是两个 object 类型对象, 但是不建议用它来比较简单类型, 它其实是用于比较两个对象是否指向同一内存地址, 因此只有比较引用对象才是有意义的. 这个方法应该设计成泛型方法才比较好, 不知道为什么要这样设计.
Assert.StartsWith Assert.EndsWith
用于断言一个字符串是否以特定字符 (串) 开头(结尾), 并且这两个方法都有一个重载用于指定是否忽略大小写
用过 Nunit 的同学可能知道, Nunit 里要实现区分大小写的 StartsWith 有点麻烦.
Assert.True Assert.False
用于断言两个布尔变量 (包括可空) 的值是否是真(假).
Assert.All
用于断言集合里的所有元素是否都满足通过测试, 奇怪的是这个方法接收的是 Action 委托而不是 Func<T,bool > 类型委托. 这将导致写出来的代码看上去有点怪异.
下面举个用于断言集合里的元素值是否都大于 0 的例子来看看如何使用它
- [Fact]
- public void Test1()
- {
- int[] intt = {3, 4, 5, 9, 22};
- Assert.All(intt, t => Assert.True(t> 0));
- }
注意以上写法, 由于不接收 Func<T,bool > 类型委托, 因此以上方法不能想当然的写为 Assert.All(intt, t => t>0); 这样将导致编译错误.
Assert.Contains
这个方法有多个重载, 功能也非常多, 但是语义都非常明确.
用于断言字符串是否包含指定字符串
相当于字符串里的 Contains, 并且表现行为类似, 也有一个重载支持不区分大小写
用于断言集合是否包含指定元素
请看以下代码
- [Fact]
- public void Test1()
- {
- int[] intt = {3, 4, 5, 9};
- Assert.Contains(4, intt);
- }
以上代码用于断言集合 intt 里是否包含元素 4, 显然是包含的
注意, 对于包含引用对象的集合判断是否包含某一元素这一个元素必须和集合中的某一个元素的引用地址一样. 这很多时候并不是我们想要的行为, 多数时候引用对象的对应的字段分别相等时我们就认为它相等, 更极端的情况是某些情况下两个对象只有某一个或者少数几个字段相等时我们也认为相等, 这要看具体实际业务. 前面讲 Nunit 时对这个问题有过详细讲解. 这里 Contains 方法同样有一个重载以支持一个比较器, 用于自定义相等性逻辑.
还有一点需要指出, Contains 方法只能断言集合是否包含某一个元素, 而不能断言是否包含某几个元素(也即一个集合是否是另一个集合的子集),Nunit 里并没有提供直接方法用于处理这样的问题. 有些同学可能认为 Assert.Subset 是用来解决这个问题的, 然而并不是, Subset 只能用于实现了 ISet 接口的集合, 很多时候并不是特别有帮助.
用于断言集合中是否包含指定类型的元素
这个重载方法语义稍显不是很明确, 它其实相当于 linq 里的 any 方法, 只要有一个 (一些) 满足条件的元素就会返回 true
- [Fact]
- public void Test1()
- {
- int[] intt = {3, 4, 5, 9};
- Assert.Contains(intt, a => a> 3);
- }
以上方法用于断言 intt 集合中是否包含大于 3 的元素, 显然是包含的.
Assert.Empty
用于断言集合是否不包含任何元素, 也即集合是否是一个空集合
Assert.Matches
此方法接收两个参数, 第一个表示要匹配的正则规则, 第二个表示要测试的字符串, 语义类似正则表达式里的 IsMatch
Assert.InRange
用于断言指定元素是否在指定的范围内
- [Fact]
- public void Test1()
- {
- Assert.InRange(20, 3, 20);
- }
以上代码片段断言 20 是否在 3 到 20 这个范围内, 显然是在的.
此方法支持一个重载接收一个 Icomparer 参数用于自定义一个比较器, 这样就可以判断任意对象是否在某一范围内, 有这方面需求的同学可以研究一下
Assert.Single
用于断言集合只包含 一个 元素, 这个方法有几个重载.
重载 1
用于断言集合中是仅包含一个元素, 这个重载可能大部分时候不是很有用
- [Fact]
- public void Test1()
- {
- Assert.Single(new[] {3});
- }
重载 2 接收一个参数, 用于断言这个元素是否是指定元素
- [Fact]
- public void Test1()
- {
- Assert.Single(new[] {3,4,5,9},3);
- }
以上代码断言集体中只有一个元素是 3
此方法相当于对集合执行 linq 的 first 方法
重载 3 接收一个 predict 类型委托, 用于断言这个元素是否满足指定条件
- [Fact]
- public void Test1()
- {
- Assert.Single(new[] {3,4,5,9},a=>a>5);
- }
以上方法断言集合中的这个是否只包含一个大于 5 的元素.
此方法相当于 linq 里面的 single 方法的有参重载
Assert.IsAssignableFrom
用于断言实例对象的类型是否是一个类型的子类(或者本身)
这个方法 Nunit 里也有, 和反射里的 IsAssignableFrom 语义相同
Assert.IsType
用于断言实例对象的类型是否是某一指定类型
和 IsAssignableFrom 相比, 此方法要求实例对象类型必须确切地是某一类型
来源: https://www.cnblogs.com/tylerzhou/p/11324902.html