.net持续集成测试篇之Nunit参数化测试
在进行单元测试的时候,很多时候,很多时候我们都是在单元测试方法内部提供特定的值,但是这样测试往往造成样本数不足从而导致覆盖的结果不够全面,很多时候我们更想提供来自外部的,满足条件的一组值来进行测试.其实Nunit框架本身提供了为测试用例提供值的能力.我们可以对它进行扩展来实现导入外部的值来填充到测试方法内部.很多朋友也自己写了不少按照一定规则生成值的方法.但是往往都是在方法内部直接调用,这样就会和单元测试的逻辑混杂在一块,导致测试方法本身不够简洁.其实可以根本测试框架本身的能力改造成为注解的方式,这样参数生成逻辑和测试逻辑一目了然.后面我们还会讲解基于Autofixture框架来生成填充数据,autofixture相比我们自己写的值填充方法,往往功能更加强大.后面我们将见证其强大之处.
提供普通参数
很容易发现,单元测试的方法都是不带参数的,有些时候我们需要为一个要测试的方法(并非单元测试方法)提供多个参数进行测试,这就会导致一个问题:我们需要写很多类似的测试方法,只是参数不一样,这样维护起来不方便,同时大量重复的工作也很烦.下面介绍Nunit里如何为测试提供参数
int Add(int x, int y)
{
return x + y;
}
以上是我们要测试的方法.
虽然Nunit测试方法正常情况下是不支持参数的,但是如果对参数添加的values注解,Nunit便会把这些参数应用到测试.
我们看一下编写的测试方法
[Test]
public void DemoTest([Values(3,4,5)]int a,[Values(6,7,8)]int b)
{
var result = Add(a, b);
Assert.AreEqual(a + b, result);
}
我们运行以上方法,可以看到测试结果通过,但是我们看一下测试面板(Test Explorer)

通过截图我们很容易发现,这个测试方法一共运行的九次!再仔细看看方法对应的参数,可以看到它是使用组合的方式把所有的可能都组合一遍.
但是有些时候我们想要的不是这样的组合,我们想要的更多时候是(3,6),(4,7),(5,8)这样的组合,如何做到呢,仍然看一段示例代码
[Test]
[Sequential]
public void DemoTest([Values(3,4,5)]int a,[Values(6,7,8)]int b)
{
var result = Add(a, b);
Assert.AreEqual(a + b, result);
}
我们看看运行结果

这次只运行了三次,并且参数的组合正如我们期待的.
这个方法和上面的一样,只是多了一个[Sequential]注解
注意Values注解里的参数都是Object类型,运行时候转换为参数的真正类型,如果无法转换则会抛出异常.比如
[Values("a")]int x由于a是字符串类型,通过内置方法无法转换为int,因些会抛出异常.
提供基于范围的参数
上面的测试Values(3,4,5)和Values(6,7,8)都是连续的数字,如果连接的参数更多,我们可以使用基于范围的参数.
看以下示例代码
[Test]
[Sequential]
public void DemoTest([Range(3,5)]int a,[Range(6,8)]int b)
{
var result = Add(a, b);
Assert.AreEqual(a + b, result);
}
我们把Values注解改为Range注解,就ok了
提供随机参数
我们还可以为测试提供一些随机数,以使测试变得更随机,覆盖范围更大
这里要使用Random注解
请看下面示例
[Test]
[Sequential]
public void DemoTest([Random(3)]int a, [Random(3)]int b)
{
var result = Add(a, b);
Assert.AreEqual(a + b, result);
}
Random的参数为要生成随机数的个数.
Random还有一重载以支持生成随机数的最大值和最小值
[Test]
[Sequential]
public void DemoTest([Random(3,10,2)]int a, [Random(5,9,3)]int b)
{
var result = Add(a, b);
Assert.AreEqual(a + b, result);
}
示例中Random的三个参数分别是最小值,最大值和个数
[info]Random的最大值和最小值不仅可以是整数,也可以是小数
提供计算参数
先看一个示例
[Test]
[Sequential]
public void DemoTest(DateTime dt1)
{
DateTime dt2 = default(DateTime);
Assert.Greater(dt1, dt2);
}
这里测试方法的参数是Datetime类型,我们如何给给它提供值呢,很多人可能会想使用Values[DateTime.Now] 来注解dt1参数,然而不幸的是Values注解只接受const类型的值,这里介绍ValueSource注解来解决这个问题.
ValueSource的机制是使用一个方法来获取值,然后提供给测试方法参数,它接受一个字符串类型的参数,用于指定提供值的方法名.
我们用以下方法生成一些DateTime值
static IEnumerable<DateTime> GetPeople()
{
yield return DateTime.Now;
yield return DateTime.Now.AddDays(2);
}
以上方法生成了一个包含两个DateTime值的集合.下面我们看如何使用它
[Test]
public void DemoTest([ValueSource(nameof(FirstUnitTest.GetPeople))]DateTime dt1)
{
DateTime dt2 = default(DateTime);
Assert.Greater(dt1, dt2);
}
我们使用nameof获取刚才生成的用于提供值的方法,作为ValueSource的参数.
使用nameof而不是使用手写字符串的好处在于nameof可以有智能提示,防止手写出现错误,另外就是如果方法名更改,这里将会抛出了一个错误,静态字符串不会提示错误,如果在运行时找不到这个方法则会抛出运行时错误
用于为ValueSource提供值的方法必须是
静态的
以上代码,我们把提供值的方法直接写在测试类里,这并不是一种很好的实践,一种好的做法是把所有的用于提供值的方法放在一个外部的类中.
我们把这个类移动到一个叫作MyValueProvider的类中
代码如下
public class MyValueProvider
{
public static IEnumerable<DateTime> GetPeople()
{
yield return DateTime.Now;
yield return DateTime.Now.AddDays(2);
}
}
单元测试方法改成如下:
[Test]
public void DemoTest([ValueSource(typeof(MyValueProvider),nameof(MyValueProvider.GetPeople))]DateTime dt1)
{
DateTime dt2 = default(DateTime);
Assert.Greater(dt1, dt2);
}
如果把值提供方法不在本类中(当前测试方法所在的类),提供一个Type类型(提供值的方法所在的类的类型)作为第一个参数,方法名作为第二个参数.
上面讲的都是基于参数注解的值提供方法,这里基于方法的注解的值提供方法.当然,它完成的功能基于参数注解的方法也同样能完成.
TestCaseAttribute注解
看以下代码片段
[TestCase(3,4)]
public void DemoTest(int x,int y)
{
var val = Add(x, y);
Assert.AreEqual(x + y, val);
}
其中用到的Add方法代码如下
int Add(int x, int y)
{
return x + y;
}
TestCase的工作原理是这样的,它提供的值是基于位置的,每一个位置处的值赋值给第一个参数,第二个位置处的值提供给第二个参数...
有了TestCase注解之后,Test注解不再是必要的.
TestCaseSourceAttribute注解
从上ValueSource我们很容易想到可能会有TestCaseSource,实际上也确实是这样的,TestCaseSource功能也同ValueSource一样,用于提供基于计算的结果.
用于提供值的类如下
public class MyValueProvider
{
public static ArrayList ar = new ArrayList
{
new int[] {3, 4},
new int[] {5, 9},
new int[] {9, 22}
};
}
测试方法如下
[TestCaseSource(typeof(MyValueProvider),nameof(MyValueProvider.ar))]
public void DemoTest(int x,int y)
{
var val = Add(x, y);
Assert.AreEqual(x + y, val);
}
从这个例子我们可看到,不仅方法可以提供值,属性,普通字段也可以提供值
为TestCaseSource提供值的字段,方法,属性也
必须是静态的
TestCase和TestCaseSource都支持多重注解,有几个注解,测试方法就会运行几次.
.net持续集成测试篇之Nunit参数化测试的更多相关文章
- .net持续集成测试篇之Nunit 测试配置
系列目录 在开始之前我们先看一个陷阱 用到的Person类如下 public class Person:IPerson { public string Name { get; set; } publi ...
- .net持续集成测试篇之Nunit常见断言
系列目录 Nunit测试基础之简单断言 在开始本篇之前需要补充一些内容,通过前面搭建Nunit测试环境我们知道要使一个方法成为单元测试方法首先要在此方法所在类加上TestFixture注解,并且在该方 ...
- .net持续集成测试篇之Nunit文件断言、字符串断言及集合断言
使用前面讲过的方法基本上能够完成工作中的大部分任务了,然而有些功能实现起来还是比较麻烦的,比如说字符串相等性比较不区分大小写,字符串是否匹配某一正则规则,集合中的每一个(某一个)元素是否符合特定规则等 ...
- .net持续集成测试篇之Nunit that断言
系列目录 that是Nunit的新语法,语义上不如简单断言,使用上也更加复杂,但是其功能更加强大. 其基本语法如下代码片段示: [Test] public void DemoTest() { bool ...
- .netcore持续集成测试篇之Xunit数据驱动测试一
系列目录 Nunit里提供了丰富的数据测试功能,虽然Xunit里提供的比较少,但是也能满足很多场景下使用了,如果数据场景非常复杂,Nunit和Xunit都是无法胜任的,有不少测试者选择自己编写一个数据 ...
- .netcore持续集成测试篇之MVC测试
前面我们讲的很多单元测试的的方法和技巧不论是在.net core和.net framework里面都是通用的,但是mvc项目里有一种比较特殊的类是Controller,首先Controller类的返回 ...
- .netcore持续集成测试篇之开篇简介及Xunit基本使用
系列目录 为了支持跨平台,微软为.net平台提供了.net core test sdk,这样第三方测试框架诸如Nunit,Xunit等只需要按照sdk提供的api规范进行开发便可以被dotnet cl ...
- .netcore持续集成测试篇之搭建内存服务器进行集成测试一
系列目录 在web项目里,我们把每一层的代码的单元测试都通过并不代表程序能正常运行,因为这个过程缺失了http管道,很多时候我们还还需要把项目布在iis环境中或者在vs里启动iis express服务 ...
- .netcore持续集成测试篇之web项目验收测试
系列目录 通过前面的单元测试,我们能够保证项目的基本模块功能逻辑是正常的,通过集成测试能够保证接口的请求是正常的.然而最终项目交付我们还需要对项目进行页面的行为进行测试,比如页面布局是否正常,按钮是否 ...
随机推荐
- Linux运维工程师学习成长路线
不过大家的留言都很精彩,希望大家也可以去留言区逛一逛~~ 好在这不是最后一期送书,之前已经有了好多活动,小编一定继续为大家多送些福利. 希望大家可以一如既往的关注脚本之家,支持爱你们的小编,共同进步! ...
- Python开发【第六篇】: 面向对象
内容概要 面向对象和面向过程 面向对象三大特征 面向对象的成员 类与类之间的关系 约束 type.issubclass.isinstance self.super.MRO 1. 面向对象和面向过程 0 ...
- 关于在使用scrapy-redis分布式踩过的那些坑:
自己的案列:win7上安装ubuntu (win7作为slaver,ubuntu作为master ) 修改配置文件redis.conf 1)打开配置文件把下面对应的注释掉 # bind 127.0.0 ...
- Spring Boot + Elasticsearch实现大批量数据集下中文的精确匹配-案例剖析
缘由 数据存储在MYSQ库中,数据基本维持不变,但数据量又较大(几千万)放在MYSQL中查询效率上较慢,寻求一种简单有效的方式提高查询效率,MYSQL并不擅长大规模数据量下的数据查询. 技术方案 考虑 ...
- Java 7 源码学习系列(二)——Enum
Enum Enum类是java.lang包中一个类,他是Java语言中所有枚举类型的公共基类. 一.定义 public abstract class Enum<E extends Enum< ...
- 剑指offer第二版-总结:二叉树的遍历
思想:前序(根左右),中序(左根右),后序(左右根) 前序非递归遍历: 首先判断根是否为空,将根节点入栈 1.若栈为空,则退出循环 2.将栈顶元素弹出,访问弹出的节点 3.若弹出的节点的右孩子不为空则 ...
- 从壹开始[ 做贡献 ]之三 || 北京.Net俱乐部活动——DNT精英论坛开幕
缘起 哈喽大家好!好久不见,可能有一部分小伙伴发现我好久没有写文章了,是不是懒惰了,并没有,这两周也是正式开始了<NetCore系列教程的视频录制>,不过还不多,预计会是每周一个视频,基本 ...
- Android开发-百度地图之电子围栏
功能实现:根据经纬度创建一个坐标,判断该坐标是否在指定圆形覆盖范围内 两个计算方法,直接套用: /** * 地球半径 */private static double EARTH_RADIUS = 63 ...
- stixel_world+Multi_stioxel_world+semantic_stixel_world知识拓展
Semantic_Stixel_World 学习笔记 因项目方向更改,该研究暂停, 转为opengl等3D渲染. Author: Ian 星期四, 20. 六月 2019 06:11下午 最近看网络上 ...
- 不同版本2.5的Servlet web.xml 头信息
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" ...