系列目录

有了上一节自定义配置,很多问题都能解决了,但是如果仅仅是为了解决一个简单问题那么创建一个类显得有点繁重.其实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. excel报表开发-- 根据datatable个数自动生成新sheet

    总结一下很久之前做的报表小程序,今日有问题又调试了一下. DB中存在一个表,记录了ID<自增长>,SqlStatement<sql查询语句>及其他必要字段,比如SheetNam ...

  2. idea 警告:Warning:java: 源值1.5已过时, 将在未来所有发行版中删除

    在pom.xml文件中添加 <properties>         <maven.compiler.source>1.8</maven.compiler.source& ...

  3. C#7.2 新增功能

    连载目录    [已更新最新开发文章,点击查看详细] C# 7.2 又是一个单点版本,它增添了大量有用的功能. 此版本的一项主要功能是避免不必要的复制或分配,进而更有效地处理值类型. C# 7.2 使 ...

  4. 小白开学Asp.Net Core《二》(补)

    小白开学Asp.Net Core<二>(补) ——数据仓储层(Repositroy).服务层(Service) -------------------------------------- ...

  5. 技术派-不用sqrt手工计算平方根

    题目:任意长度数串,不使用sqrt函数,手工计算平方根?   要求只准用加/减/乘/除四则运算,不准使用power/sqrt等函数.   算法如下: 1.以小数点为中心往两边每2位分隔为一组: 2.然 ...

  6. springmvc+mybatis+spring+redis

    只作参考,以防忘记使用! mybatis的配置文件: <?xml version="1.0" encoding="UTF-8" ?> <!DO ...

  7. Apache Ignite 学习笔记(6): Ignite中Entry Processor使用

    之前的文章我们其实已经用到了两种不同的方式访问Ignite中的数据.一种方式是第一篇文章中提到通过JDBC客户端用SQL访问数据,在这篇文章中我们也会看到不使用JDBC,如何通过Ignite API用 ...

  8. C# 委托(delegate)、泛型委托和Lambda表达式

    目录 # 什么是委托 # 委托声明.实例化和调用 1.声明 2.委托的实例化 3.委托实例的调用 4.委托完整的简单示例 #泛型委托 1.Func委托 2.Action委托 3.Predicate委托 ...

  9. Chrome 跨域 disable-web-security 关闭安全策略

    谷歌浏览器暂时关闭跨域. 当遇到以下情况,则可以简单的使用 关闭Chrome 安全策略跨域 开发时跨域,上线后,部署在一个域名下没有跨域问题 开发时,临时解决跨域问题 只有开发时用这个,其他时候,就不 ...

  10. 请使用switch语句和if...else语句,计算2008年8月8日这一天,是该年中的第几天。

    请使用switch语句和if...else语句,计算2008年8月8日这一天,是该年中的第几天. #include <stdio.h> int main() { /* 定义需要计算的日期 ...