.net测试篇之测试神器Autofixture Generator使用与自定义builder
有了上一节自定义配置,很多问题都能解决了,但是如果仅仅是为了解决一个简单问题那么创建一个类显得有点繁重.其实AutoFixture在创建Fixture对象时有很多方便的Fluent配置,我们这里介绍一些比较常用了.
创建对象是忽略一些属性
有些时候有这样的一些业务场景,有些字段是非必填项,但是一旦填写则必须符合指定规则.这些非必填字段在业务中仅仅当它存在的时候做一些校验,其它地方并没有使用到它.这样在单元测试的时候我们为了效率可以暂时忽略这些字段.在后面集成测试的时候再提供完整数据.
下面看看AutoFixture在生成对象的时候如何显式地忽略一些字段
之所以要忽略是因为如果不忽略AutoFixture自动为字符串类型生成一个guid字符串,这将会导致验证失败.
我们扩展一下Person类,代码如下
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public DateTime BirthDay { get; set; }
public string Mobile { get; set; }
public string IDCardNo { get; set; }
public string Email { get; set; }
}
我们看配置代码
[Test]
public void FixValueTest()
{
var fix = new Fixture();
var psn = fix.Build<Person>().
Without(a => a.IDCardNo).
Create();
}
这里fix对象使用Build方法,生成一个自定义生成对象,然后会出现很多自定义配置方法,我们使用without方法指示AutoFixture在生成时不生成某一字段,一个without后面还可以再接一个,如果需要忽略其它字段,可以串联使用多个without.
指定当前时间
在集成测试的时候,有些关于时间的字段都需要是当前时间,这时候可以使用AutoFixture内置的自定义类CurrentDateTimeGenerator来实现
[Test]
public void FixValueTest()
{
var fix = new Fixture();
fix.Customizations.Add(new CurrentDateTimeGenerator());
var psn = fix.Create<Person>();
}
[info]当然以上配置可能是没有必要的,因为C#很早就支持属性赋初值了.
UriGenerator
此配置可以让AutoFixture生成一个Uri
[Test]
public void FixValueTest()
{
var fix = new Fixture();
fix.Customizations.Add(new UriGenerator());
var br = fix.Create<Uri>()# AutoFixture配置二
有了上一节自定义配置,很多问题都能解决了,但是如果仅仅是为了解决一个简单问题那么创建一个类显得有点繁重.其实AutoFixture在创建Fixture对象时有很多方便的Fluent配置,我们这里介绍一些比较常用了.
## 创建对象是忽略一些属性
有些时候有这样的一些业务场景,有些字段是非必填项,但是一旦填写则必须符合指定规则.这些非必填字段在业务中仅仅当它存在的时候做一些校验,其它地方并没有使用到它.这样在单元测试的时候我们为了效率可以暂时忽略这些字段.在后面集成测试的时候再提供完整数据.
下面看看AutoFixture在生成对象的时候如何显式地忽略一些字段
>之所以要忽略是因为如果不忽略AutoFixture自动为字符串类型生成一个guid字符串,这将会导致验证失败.
我们扩展一下Person类,代码如下
```cs
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public DateTime BirthDay { get; set; }
public string Mobile { get; set; }
public string IDCardNo { get; set; }
public string Email { get; set; }
}
我们看配置代码
[Test]
public void FixValueTest()
{
var fix = new Fixture();
var psn = fix.Build<Person>().
Without(a => a.IDCardNo).
Create();
}
这里fix对象使用Build方法,生成一个自定义生成对象,然后会出现很多自定义配置方法,我们使用without方法指示AutoFixture在生成时不生成某一字段,一个without后面还可以再接一个,如果需要忽略其它字段,可以串联使用多个without.
指定当前时间
在集成测试的时候,有些关于时间的字段都需要是当前时间,这时候可以使用AutoFixture内置的自定义类CurrentDateTimeGenerator来实现
[Test]
public void FixValueTest()
{
var fix = new Fixture();
fix.Customizations.Add(new CurrentDateTimeGenerator());
var psn = fix.Create<Person>();
}
[info]当然以上配置可能是没有必要的,因为C#很早就支持属性赋初值了.
UriGenerator
此配置可以让AutoFixture生成一个Uri
[Test]
public void FixValueTest()
{
var fix = new Fixture();
fix.Customizations.Add(new UriGenerator());
var br = fix.Create<Uri>();
}
MailAddressGenerator
用于生成邮箱地址
[Test]
public void FixValueTest()
{
var fix = new Fixture();
fix.Customizations.Add(new MailAddressGenerator());
var mr = fix.Create<MailAddress>()
}
当然很多时候我们是想要MailAddress里的字符串.这时候使用MailAddress的Address属性即可.
;
}
## MailAddressGenerator
用于生成邮箱地址
```cs
[Test]
public void FixValueTest()
{
var fix = new Fixture();
fix.Customizations.Add(new MailAddressGenerator());
var mr = fix.Create<MailAddress>()
}
当然很多时候我们是想要MailAddress里的字符串.这时候使用MailAddress的Address属性即可.
AutoFixture结合DataAnnotations
有些时候有这样一些场影,我们的实体类中有很多验证注解,这就对制造出的假数据有很多要求,比如必须符合邮箱号,身份证号,字符串长度必须为特定值,手机号必须为特定长度数字等等.通过前面讲到的自定义配置我们可以实现以上功能,但是如果仅仅是为了生成一个指定长度的字符串我们再新建一个配置类实在有点繁琐,并且这些逻辑也并非都是通用的.更为复杂的是有时候一个特定字段必须符合某一正则规则,如果这个规则非常复杂想要生成满足它的假数据确实需要花费些心思,这时候如果AutoFixture能自动生成满足条件的假数据那该有好多,实际上AutoFixture确实可以生成满足DataAnnotations约束的字段,并且不需要配置默认就是支持的.
比如现在Person类改为如下:
public class Person{
[StringLength(10)]
public string Name { get; set; }
[Range(18,42)]
public int Age { get; set; }
public DateTime BirthDay { get; set; }
[RegularExpression("\\d{11}")]
public string Mobile { get; set; }
}
AutoFixture会自动生成满足条件的字段.
AutoFixture 生成符合特定业务的字段.
Attribute自动生成符合注解约束的字段为集成测试提供了很大方便.然而一些功能不论是注解还是AutoFixture内置的配置都无法完成,这时候就需要自定义的配置.
比如说有以下业务场景,有些业务模型带有开始时间和结束时间,这里就有一个隐性约束就是结束时间必须大于或者等于开始时间,如果不是这样数据库中就无法取到值.这个时候就必须使用自定义配置了.
比如有一下模型:
public class CustomDate
{
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
}
[info]这里只是一个普通例子,很多时候业务里面都有这样的模型,如果要让结束时间晚于开始时间,我们首先要先确定哪个时开始时间,哪个是结束时间,这里我们使用基于命名约束的方法来确定它们:即开始时间带有start,结束时间带有end(当然也可以是其它标识,只要能确定它们即可).当然这并不是一种很好的设计.理想的情况下是对字段进行注解,但是仅仅为了测试而去扩展现有项目的做法也是值得商榷的.
以下为自定义方法
public class DateTimeSpecimenBuilder:ISpecimenBuilder
{
private readonly Random _random = new Random();
private DateTime startDate = DateTime.Now;
public object Create(object request, ISpecimenContext context)
{
var pi = request as PropertyInfo;
if (pi != null && pi.Name.ToLower().Contains("start") &&
(pi.PropertyType == typeof(DateTime) || pi.PropertyType == typeof(DateTime?)))
{
var stDate = context.Create<DateTime>();
startDate =stDate ;
return startDate;
}
if (pi != null && pi.Name.ToLower().Contains("end") &&
(pi.PropertyType == typeof(DateTime) || pi.PropertyType == typeof(DateTime?)))
{
var endDate = startDate.AddDays(_random.Next(1,20));
return endDate;
}
return new NoSpecimen();
}
}
[Test]
public void FixValueTest()
{
var fix = new Fixture();
fix.Customizations.Add(new DateTimeSpecimenBuilder());
var customDate = fix.Create<CustomDate>();
}
简单梳理一下以上代码,基本逻辑就是如果传入字段包含start关键特征并且是日期类型,我们就把它的值存起来,当后面遇到包含end特征的属性时就把刚才存入的值再加上指定天数这样就能保证enddate大于startdate了.
生成引用类型时指定构造函数
当一个类有多个构造函数时,AutoFixture默认使用参数最少的构造函数来构造一个对象,但是这在有些时候会造成麻烦:一个Bll类可能有多个构造函数,构造函数里传入的是依赖对象,如果只调用参数最少的构造函数则很多依赖无法传入,这样如果使用到了依赖对象就会报Null引用异常.这个时候我们就需要显式的指定调用哪一个构造函数.
我们仍然通过示例来讲解.
我们把Person类改成如下:
public class Person
{
public Person(string name)
{
Name = name;
}
public Person(string name,int age)
{
Age = age;
Name = name;
}
[StringLength(10)]
public string Name { get; set; }
[Range(18,42)]
public int Age { get; set; }
public DateTime BirthDay { get; set; }
[RegularExpression("\\d{11}")]
public string Mobile { get; set; }
public string IDCardNo { get; set; }
与之前相比,这个类多了两个有参构造函数.
注意,即使类型不包含无参构造函数,AutoFixture依然能够创建它,只是使用哪个构造函数是不确定的.
要实现让AutoFixture选择我们想要的构造函数,我们创建一个实现了IMethodQuery的类型,然后添加到配置里.
这个类代码如下
public class MyMethodSelector : IMethodQuery
{
private readonly Type[] _types;
public MyMethodSelector(params Type[] type)
{
_types = type;
}
public IEnumerable<IMethod> SelectMethods(Type type)
{
if (type == null)
{
throw new ArgumentNullException();
}
var constructors = type
.GetConstructors().Where(a => a.GetParameters().Select(t => t.ParameterType).SequenceEqual(_types))
.Select(a => new ConstructorMethod(a));
return constructors;
}
}
我们来分析一下这段代码,首先我们在构造函数里传入type 数组,这里的type为构造函数参数的类型.SelectMethods为接口提供的方法,这个方法接收一个type类型作为参数,这个type为操作的对象的类型.然后我们使用GetConstructors方法获取它所有的构造函数.然后通过Where过滤符合条件的(条件是参数的类型和构造函数传入的类型一样).然后我们使用过滤后的Constructorinfo来创建一个ConstructorMethod,ConstructorMethod为AutoFixture提供的一个类型.
下面是测试代码
var fix = new Fixture();
fix.Customize(new ConstructorCustomization(typeof(Person),
new MyMethodSelector(typeof(string), typeof(int))));
var psn = fix.Create<Person>();
这里我们给MyMethodSelector传入了两个类型,分别是string类型和int类型,以期望AutoFixture调用包含string和int参数的构造方法.我们启用调试模式,就可以看到Person的第二个构造函数被调用了.
看到这里,很多人可能会感觉有些厌烦,感觉这样做还不如直接New一个对象,在new的时候显式调用特定的构造函数就不会有这么麻烦了.关于直接new对象的缺点前面也说过,如果Bll层有变动,则需要显式修改测试方法,不利于维护,并且这个方法是可以通用的.一旦创建好之后以后遇到这样的业务就可以直接调用了.
.net测试篇之测试神器Autofixture Generator使用与自定义builder的更多相关文章
- .net测试篇之测试神器Autofixture基本配置一
系列目录 实际工作中我们需要的数据逻辑万千,千变万化,而AutoFixture默认是按照一定算法随机生成一些假数据,虽然这在多数时候是ok的,但是可能不能满足我们的所有业务场景,有些时候我们需要进行一 ...
- .net测试篇之测试神器Autofixture在几个复杂场景下的使用示例以及与Moq结合
系列目录 为String指定一个值. 在第三节里我们讲了如何使用自定义配置加上一个自定义算法生成一个自定义字符串,然而有些时候我们仅仅是需要某个字段是有意义的,这个时候随便生成的字符串也满足不了我们的 ...
- Maven测试篇
maven的生命周期: 讲解Maven测试篇之前将首先介绍一下Maven生命周期的相关概念,如果你熟知这部分概念可以略过此小节内容. 大多数时候,我们在构建一个项目时,不外乎是对其进行清理.编译.测 ...
- Java自动化测试框架-11 - TestNG之annotation与并发测试篇 (详细教程)
1.简介 TestNG中用到的annotation的快速预览及其属性. 2.TestNG基本注解(注释) 注解 描述 @BeforeSuite 注解的方法只运行一次,在当前suite所有测试执行之前执 ...
- 十一、Abp vNext 基础篇丨测试
前言 祝大家国庆快乐,本来想国庆之前更新完的,结果没写完,今天把剩下的代码补了一下总算ok了. 本章节也是我们后端日常开发中最重要的一步就是测试,我们经常听到的单元测试.集成测试.UI测试.系统测试, ...
- 项目Alpha冲刺(团队)-测试篇
格式描述 课程名称:软件工程1916|W(福州大学) 作业要求:项目Alpha冲刺(团队)-代码规范.冲刺任务与计划 团队名称:为了交项目干杯 测试用例:测试用例文档.zip 作业目标:描述项目的测试 ...
- <转>iOS应用程序内使用IAP/StoreKit付费、沙盒(SandBox)测试、创建测试账号流程!
原文地址:http://blog.csdn.net/xiaominghimi/article/details/6937097 //——2012-12-11日更新 获取"产品付费数量等于0 ...
- app测试与web测试的区别
1.从功能测试的来讲的话,在流程和功能测试上是没有区别的.系统测试和一些细节可能会不一样. 那么我们就要先来了解,web和app的区别. web项目,一般都是b/s架构,基于浏览器的,而app则是c/ ...
- 用 Python 测试框架简化测试
用 Python 测试框架简化测试 摘要:本文将向您介绍了三种流行 Python 测试框架(zope.testing,py.test,nose)的基本特性,并讨论新一代的测试风格. 最近出现了行业级的 ...
随机推荐
- python整型-浮点型-字符串-列表及内置函数(上)
整型 简介 # 是否可变类型: 不可变类型 # 作用:记录年龄.手机号 # 定义: age = 18 # --> 内部操作 age = int(18) # int('sada') # 报错 in ...
- 第一章jQuery基础
一.jQuert简介 1.什么是jQuery jQuery是javaScript的程序库之一,它是javaScript对象和实用函数的封装. jQuery是继Prototype之后又一个优秀的java ...
- 在Linux - Centos上安装Python3(上)
必看内容 在Linux上安装Python常用的2种方法 1.Python源码编译安装,有点复杂,适合老司机 2.从EPEL/IUS仓库安装,新手建议使用些方法,比较简单,目前2019-07-31提供最 ...
- 理解SVG中的 viewport,viewBox, preserveAspectRatio
_ 阅读目录 一:理解viewport 二:理解viewBox 三:理解 preserveAspectRatio 回到顶部 一:理解viewport 该属性表示的是SVG可见区域的大小.或者也可以叫画 ...
- 二进制文件安装安装etcd
利用二进制文件安装安装etcd etcd组件作为一个高可用强一致性的服务发现存储仓库. etcd作为一个受到ZooKeeper与doozer启发而催生的项目,除了拥有与之类似的功能外,更专注于以下四点 ...
- 【Machine Learning·机器学习】决策树之ID3算法(Iterative Dichotomiser 3)
目录 1.什么是决策树 2.如何构造一棵决策树? 2.1.基本方法 2.2.评价标准是什么/如何量化评价一个特征的好坏? 2.3.信息熵.信息增益的计算 2.4.决策树构建方法 3.算法总结 @ 1. ...
- django+uwsgi+nginx 部署生产环境
一.Uwsgi安装 python3 -m pip install uwsgi cp /usr/local/python3/bin/uwsgi /usr/bin/ 测试 在django项目主目录下cre ...
- 用python实现九九乘法表输出-两种方法
2019-08-05 思考过程:九九乘法表需要两层循环,暂且称之为内循环和外循环,因此需要写双层循环来实现. 循环有for和while两种方式. for循环的实现 for i in range(1,1 ...
- IT技术管理者的自我修养
1. 前言 本来写<IT技术管理者的自我修养>与<IT技术人员的自我修养>是一开始就有的想法.但发表<IT技术人员的自我修养>后,收到了不少良好的反馈,博客园的编辑 ...
- adb 下载安装
1.官网下载:选择自己电脑对应的版本 https://www.androiddevtools.cn/# SDK Tools, SDK platfrom Tools(解压在sdk 的根目录下) 2 ...