系列目录

有了上一节自定义配置,很多问题都能解决了,但是如果仅仅是为了解决一个简单问题那么创建一个类显得有点繁重.其实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的更多相关文章

  1. .net测试篇之测试神器Autofixture基本配置一

    系列目录 实际工作中我们需要的数据逻辑万千,千变万化,而AutoFixture默认是按照一定算法随机生成一些假数据,虽然这在多数时候是ok的,但是可能不能满足我们的所有业务场景,有些时候我们需要进行一 ...

  2. .net测试篇之测试神器Autofixture在几个复杂场景下的使用示例以及与Moq结合

    系列目录 为String指定一个值. 在第三节里我们讲了如何使用自定义配置加上一个自定义算法生成一个自定义字符串,然而有些时候我们仅仅是需要某个字段是有意义的,这个时候随便生成的字符串也满足不了我们的 ...

  3. Maven测试篇

     maven的生命周期: 讲解Maven测试篇之前将首先介绍一下Maven生命周期的相关概念,如果你熟知这部分概念可以略过此小节内容. 大多数时候,我们在构建一个项目时,不外乎是对其进行清理.编译.测 ...

  4. Java自动化测试框架-11 - TestNG之annotation与并发测试篇 (详细教程)

    1.简介 TestNG中用到的annotation的快速预览及其属性. 2.TestNG基本注解(注释) 注解 描述 @BeforeSuite 注解的方法只运行一次,在当前suite所有测试执行之前执 ...

  5. 十一、Abp vNext 基础篇丨测试

    前言 祝大家国庆快乐,本来想国庆之前更新完的,结果没写完,今天把剩下的代码补了一下总算ok了. 本章节也是我们后端日常开发中最重要的一步就是测试,我们经常听到的单元测试.集成测试.UI测试.系统测试, ...

  6. 项目Alpha冲刺(团队)-测试篇

    格式描述 课程名称:软件工程1916|W(福州大学) 作业要求:项目Alpha冲刺(团队)-代码规范.冲刺任务与计划 团队名称:为了交项目干杯 测试用例:测试用例文档.zip 作业目标:描述项目的测试 ...

  7. <转>iOS应用程序内使用IAP/StoreKit付费、沙盒(SandBox)测试、创建测试账号流程!

    原文地址:http://blog.csdn.net/xiaominghimi/article/details/6937097 //——2012-12-11日更新   获取"产品付费数量等于0 ...

  8. app测试与web测试的区别

    1.从功能测试的来讲的话,在流程和功能测试上是没有区别的.系统测试和一些细节可能会不一样. 那么我们就要先来了解,web和app的区别. web项目,一般都是b/s架构,基于浏览器的,而app则是c/ ...

  9. 用 Python 测试框架简化测试

    用 Python 测试框架简化测试 摘要:本文将向您介绍了三种流行 Python 测试框架(zope.testing,py.test,nose)的基本特性,并讨论新一代的测试风格. 最近出现了行业级的 ...

随机推荐

  1. 跨站脚本攻击(selfxss)笔记(三)

    本篇纯文字发表自己对自插型xss的看法 selfxss,顾名思义,自己输入xss脚本,输出仅自己看到,仅xss到自己. 常见的有:查询框的xss.某个账号中,添加自己的分组类等 查询框的xss: 即在 ...

  2. 比赛:小奔与不等四边形solution

    题目: 题目背景 有这样一道经典的数学题:已知一个四边形的边长是四个连续的正整数,求证这个四边形的面积的最大值不为整数.小奔轻松地证明了这个问题,现在问题来了,大奔要求小奔以最快的速度算出给定边长的四 ...

  3. CF1027D Mouse Hunt题解

    题目: 伯兰州立大学的医学部刚刚结束了招生活动.和以往一样,约80%的申请人都是女生并且她们中的大多数人将在未来4年(真希望如此)住在大学宿舍里. 宿舍楼里有nn个房间和一只老鼠!女孩们决定在一些房间 ...

  4. 新手小白快速登天日记之安装python以及环境变量和pycharm解释器较为详细教程

    Python解释器安装及环境变量配置 Python官方网站:www.python.org 首先打开这个网址,找到downloads选择,因为我是Windows所以下载windows的,因电脑而异 然后 ...

  5. fiddle知识点六、如何使用fiddle进行模拟弱网

    为什么要模拟弱网 随着互联网的快速发展,越来越多的应用核心功能需要网络进行实现.同一应用在2G.3G.4G和WiFi的不停网络下,响应各有不同.但是因为现在的网络普遍为4G网络,为了保证应用在不同的网 ...

  6. 正则与sed,grep,awk三剑客

    系统登录顺序: /etc/profile /etc/profile.d/a.sh (a.sh自己建的) /root/.bash_profile /root/.bashrc /etc/bashrc /b ...

  7. UltraEdit不自动生成保存备份文件(.bak)

    UltraEdit修改文件或格式化文件保存后会生成烦人的.bak文件. 去掉该功能办法如下: 高级 -> 配置 -> 文件处理 -> 备份 “保存时备份文件”选择“不备份” (Adv ...

  8. bootstrap datatable editor 扩展

    需求: a. 表单样式更改. b. 表单大小更改. 思路: a. 通过设置modal css更改样式和大小.缺点,全局性的更改. b. 更改bootstrap-editor,可以通过某种方式将参数传入 ...

  9. http状态码 400-499

    类比 服务器:便利店 客户端:客人 http报文:中文语言+钱 400-499 客户的错误 400 :服务器不理解客服端请求的意思是什么,如请求报文损坏 举例: 客户端:@#!3&* 服务器: ...

  10. 通过ping命令了解三层转发流程

    ping命令:因特网包探索器.本文主要通过路由器两端不同网段PC互ping来讲解三层转发流程. 例子:PC-A是如何 ping 通 PC-C 的,有几种情况? 说明:1.在条件1阶段PC-C不会刷新a ...