引言

在我们之前的文章中介绍过使用Bogus生成模拟测试数据,今天来讲解一下功能更加强大自动生成测试数据的工具的库"AutoFixture"

什么是AutoFixture?

AutoFixture 是一个针对 .NET 的开源库,旨在最大程度地减少单元测试中的“安排(Arrange)”阶段,以提高可维护性。它的主要目标是让开发人员专注于被测试的内容,而不是如何设置测试场景,通过更容易地创建包含测试数据的对象图,从而实现这一目标。

AutoFixture 可以帮助开发人员自动生成测试数据,减少手动设置测试数据的工作量,提高单元测试的效率和可维护性。通过自动生成对象,开发人员可以更专注于编写测试逻辑,而不必花费大量精力在准备测试数据上。

其实和Bogus相比,AutoFixture更强大的地方在于可以自动化设置对象的值,当类发生变化时如属性名或者类型更改,我们不需要去进行维护,AutoFixture可以自动适应Class的变化。

AutoFixture与流行的 .NET 测试框架(如 NUnitxUnit)可以无缝集成。

AutoFixture实战

我们在创建xUnit单元测试项目dotNetParadise.AutoFixture

安装依赖

创建完项目之后我们首先要安装Nuget

PM> NuGet\Install-Package AutoFixture -Version 4.18.1

初始化

AutoFixture的使用是从一个Fixture的实例对象开始的

var fixture = new Fixture();

接下来我们先创建一个测试类来学一下AutoFixture的使用

public class AutoFixtureStaffTest
{
private readonly IFixture _fixture;
public AutoFixtureStaffTest()
{
_fixture = new Fixture();
}
}

实战

我们之前的测试项目创建了Sample.ApiSample.Repository两个类库来做我们被测试的项目,本章继续使用Sample.Repository来演示AutoFixture的使用。

dotNetParadise.AutoFixture 测试项目添加Sample.Repository的项目引用

Sample.Repository中我们有一个Staff的实体对象,继续用作我们的测试

public class Staff
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public int? Age { get; set; }
public List<string>? Addresses { get; set; }
public DateTimeOffset? Created { get; set; }
public void Update(Staff staff)
{
this.Name = staff.Name;
this.Email = staff.Email;
this.Age = staff.Age;
this.Addresses = staff.Addresses;
Created = staff.Created;
}
}

属性赋值

   [Fact]
public void Staff_SetProperties_ValuesAssignedCorrectly()
{
//Arrange
Staff staff = new Staff();
//生成Int类型
staff.Id = _fixture.Create<int>();
//生成string 类型
staff.Name = _fixture.Create<string>();
//生成DateTimeOffset类型
staff.Created = _fixture.Create<DateTimeOffset>();
//生成 List<string>?
staff.Addresses = _fixture.CreateMany<string>(Random.Shared.Next(1, 100)).ToList();
//Act
//...省略
// Assert
Assert.NotNull(staff); // 验证 staff 对象不为 null // 验证 staff.Id 是 int 类型
Assert.IsType<int>(staff.Id); // 验证 staff.Name 是 string 类型
Assert.IsType<string>(staff.Name); // 验证 staff.Created 是 DateTimeOffset? 类型
Assert.IsType<DateTimeOffset>(staff.Created); // 验证 staff.Addresses 是 List<string> 类型
Assert.IsType<List<string>>(staff.Addresses); // 验证 staff.Addresses 不为 null
Assert.NotNull(staff.Addresses); // 验证 staff.Addresses 中的元素数量在 1 到 100 之间
Assert.InRange(staff.Addresses.Count, 1, 100);
}

示例中用到 AutoFixture 提供的的方法随机分配随机值,上面的示例中用到使用到了两个方法

Create<T>方法

  • 用于生成一个指定类型 T 的实例。它会自动填充对象的属性和字段,以便创建一个完整的对象实例。
  • 这个方法通常用于生成单个对象实例,适用于需要单个对象作为测试数据的情况。
  • 当调用 Create<T> 方法时,AutoFixture 会根据 T 类型的构造函数、属性和字段来自动生成合适的值,以确保对象实例的完整性和一致性。

CreateMany<T>方法

  • 用于生成多个指定类型 T 的实例,通常用于生成集合或列表类型的测试数据。
  • 这个方法允许你指定要生成的实例数量,并返回一个包含这些实例的 IEnumerable 集合。
  • 当调用 CreateMany<T> 方法时,AutoFixture 会根据 T 类型的构造函数、属性和字段来生成指定数量的对象实例,以便填充集合或列表。

T包括基本类型(如 stringint)、自定义对象等

Create<T>构造对象

上面的例子我们自己实例化的对象然后对对象挨个赋值,目的是让大家对AutoFixture的使用有一个初步的认识,上面也解释到了Create<T>的泛型参数T可以是自定义的对象,那么我们来简化一下上面的示例

[Fact]
public void Staff_ObjectCreation_ValuesAssignedCorrectly()
{
// Arrange
Staff staff = _fixture.Create<Staff>(); // 使用 AutoFixture 直接创建 Staff 对象 // Act
//...省略 // Assert
Assert.NotNull(staff); // 验证 staff 对象不为 null // 验证 staff.Id 是 int 类型
Assert.IsType<int>(staff.Id); // 验证 staff.Name 是 string 类型
Assert.IsType<string>(staff.Name); // 验证 staff.Created 是 DateTimeOffset? 类型
Assert.IsType<DateTimeOffset>(staff.Created); // 验证 staff.Addresses 是 List<string> 类型
Assert.IsType<List<string>>(staff.Addresses); // 验证 staff.Addresses 不为 null
Assert.NotNull(staff.Addresses); // 验证 staff.Addresses 中的元素数量在 1 到 100 之间
Assert.InRange(staff.Addresses.Count, 1, 100);
}

修改后的例子中,我们使用 AutoFixtureCreate<Staff>() 方法直接创建了一个 Staff 对象,而不是手动为每个属性赋值。这样可以更简洁地生成对象实例。

数据驱动测试

在正常的同一个测试方法中使用不同的输入数据进行测试时,通常都是基于 Theory 属性配合InlineData或者MemberData来完成的,有了AutoFixture之后数据也不用我们自己造了,来看一下实战入门

第一步Nuget安装依赖

PM> NuGet\Install-Package AutoFixture.Xunit2 -Version 4.18.1

[AutoData]属性

[Theory, AutoData]
public void Staff_Constructor_InitializesPropertiesCorrectly(
int id, string name, string email, int? age, List<string> addresses, DateTimeOffset? created)
{
// Act
var staff = new Staff { Id = id, Name = name, Email = email, Age = age, Addresses = addresses, Created = created }; // Assert
Assert.Equal(id, staff.Id);
Assert.Equal(name, staff.Name);
Assert.Equal(email, staff.Email);
Assert.Equal(age, staff.Age);
Assert.Equal(addresses, staff.Addresses);
Assert.Equal(created, staff.Created);
}

通过 AutoData 方法,测试方法的参数化设置变得更加简单和高效,使得编写参数化测试方法变得更加容易。

[InlineAutoData]属性

如果我们有需要提供的特定化参数,可以用[InlineAutoData]属性,具体使用可以参考如下案例

    [Theory]
[InlineAutoData(1)]
[InlineAutoData(2)]
[InlineAutoData(3)]
[InlineAutoData]
public void Staff_ConstructorByInlineData_InitializesPropertiesCorrectly(
int id, string name, string email, int? age, List<string> addresses, DateTimeOffset? created)
{
// Act
var staff = new Staff { Id = id, Name = name, Email = email, Age = age, Addresses = addresses, Created = created }; // Assert
Assert.Equal(id, staff.Id);
Assert.Equal(name, staff.Name);
Assert.Equal(email, staff.Email);
Assert.Equal(age, staff.Age);
Assert.Equal(addresses, staff.Addresses);
Assert.Equal(created, staff.Created);
}

自定义对象属性值

AutoFixtureBuild 方法结合 With 方法可以用于自定义对象的属性值

    [Fact]
public void Staff_SetCustomValue_ShouldCorrectly()
{
var staff = _fixture.Build<Staff>()
.With(_ => _.Name, "Ruipeng")
.Create();
Assert.Equal("Ruipeng", staff.Name);
}

禁用属性自动生成

AutoFixture 中,可以使用 OmitAutoProperties 方法来关闭自动属性生成,从而避免自动生成属性值。这在需要手动设置所有属性值的情况下很有用。

    [Fact]
public void Test_DisableAutoProperties()
{
// Arrange
var fixture = new Fixture();
var sut = fixture.Build<Staff>()
.OmitAutoProperties()
.Create(); // Assert
Assert.Equal(0, sut.Id); // 验证 Id 属性为默认值 0
Assert.Null(sut.Name); // 验证 Name 属性为 null
Assert.Null(sut.Email); // 验证 Email 属性为 null
Assert.Null(sut.Age); // 验证 Age 属性为 null
Assert.Null(sut.Addresses); // 验证 Addresses 属性为 null
Assert.Null(sut.Created); // 验证 Created 属性为 null
}

Do 方法执行自定义操作

Do 方法是 AutoFixture 中用于执行操作的方法,通常结合 Build 方法一起使用,用于在构建对象时执行自定义操作。让我详细解释一下 Do 方法的用法和作用:

主要特点:

  • 执行操作:Do 方法允许在对象构建过程中执行自定义操作,例如向集合添加元素、设置属性值等。
  • 链式调用:可以通过链式调用多个 Do 方法,依次执行多个操作。
  • 灵活定制:通过 Do 方法,可以在对象构建过程中灵活地定制对象的属性值或执行其他操作。

    使用方法:
  • 结合 Build 方法:通常与 Build 方法一起使用,用于为对象构建器执行操作。
  • 执行自定义操作:在 Do 方法中传入一个 lambda 表达式,可以在 lambda 表达式中执行需要的操作。
  • 链式调用:可以多次调用 Do 方法,实现多个操作的顺序执行。
   [Fact]
public void Test_UpdateMethod()
{
// Arrange
var fixture = new Fixture();
var staff1 = fixture.Create<Staff>();
var staff2 = fixture.Create<Staff>(); // 使用 Do 方法执行自定义操作
var staff3 = fixture.Build<Staff>()
.Do(x => staff1.Update(staff2))
.Create(); // Assert
Assert.Equal(staff2.Name, staff1.Name); // 验证 Name 是否更新
Assert.Equal(staff2.Email, staff1.Email); // 验证 Email 是否更新
Assert.Equal(staff2.Age, staff1.Age); // 验证 Age 是否更新
Assert.Equal(staff2.Addresses, staff1.Addresses); // 验证 Addresses 是否更新
Assert.Equal(staff2.Created, staff1.Created); // 验证 Created 是否更新
}

创建三个对象,在第三个创建过程中把第一个的对象属性用第二个对象的属性覆盖。

Customize Type 自定义类型

使用自定义类型构建器来执行复杂的初始化,并且保证了创建多个相同的实例中要保持一致的自定义行为。

首先我们可以在我们的测试类构造函数中定义一个自定义规则

    public AutoFixtureStaffTest()
{
_fixture = new Fixture();
_fixture.Customize<Staff>(composer => composer.With(x => x.Email, "zhangsan@163.com"));
}

比如我设置了所有的 email 都叫zhangsan@163.com

    [Fact]
public void Test_StaffNameIsJohnDoe()
{
// Arrange
Staff staff = _fixture.Create<Staff>(); // Act // Assert
Assert.Equal("zhangsan@163.com", staff.Email);
}

这个位置大概得思想就是这样,保证用到的多实例都有相同的行为,可以参考:

使用 AutoFixture 自定义类型的生成器

Auto-Mocking with Moq

第一步安装Nuget

PM> NuGet\Install-Package AutoFixture.AutoMoq -Version 4.18.1

[Fact]
public async Task Repository_Add_ShouleBeSuccess()
{
_fixture.Customize(new AutoMoqCustomization());
var repoMock = _fixture.Create<IStaffRepository>();
Assert.NotNull(repoMock);
}

创建 Fixture 实例并使用 AutoMoqCustomization 进行定制化,以便自动模拟 Moq 对象。

使用 Create<IInterface>() 方法创建一个可分配给 IInterface 接口的模拟实例。

Auto-configured Mocks

官网示例:

fixture.Customize(new AutoMoqCustomization { ConfigureMembers = true });
fixture.Inject<int>(1234); var document = fixture.Create<IDocument>();
Console.WriteLine(document.Id); // 1234

当将 ConfigureMembers = true 添加到 AutoMoqCustomization 中时,不仅会作为自动模拟容器,还会自动配置所有生成的模拟对象,使其成员返回 AutoFixture 生成的值。

使用 Inject<int>(1234) 将整数值 1234注入到 Fixture 中。

使用 Create<IDocument>() 创建一个 IDocument 接口的实例,并输出其 Id 属性值。

更多

Moq 框架中存在一些限制,其中自动配置模式无法设置具有 ref参数的方法,并且也无法配置泛型方法。然而,您可以使用 ReturnsUsingFixture 扩展方法轻松地设置这些方法。

官网示例:

converter.Setup(x => x.Convert<double>("10.0"))
.ReturnsUsingFixture(fixture);

在这个示例中,使用 ReturnsUsingFixture 扩展方法手动设置了一个名为 Convert 的泛型方法的行为。

当调用 Convert 方法并传入字符串"10.0" 时,ReturnsUsingFixture方法将使用fixture生成的值作为返回值。 通过使用ReturnsUsingFixture扩展方法,您可以绕过Moq框架的限制,手动设置具有ref` 参数或泛型方法的行为,以满足特定的测试需求.

最后

AutoFixture就像是一个自动数据生成器,让我们的单元测试变得更简单、更高效。通过使用它,我们可以轻松地创建测试数据,专注于写好测试逻辑,而不用为数据准备的琐事烦恼.

欢迎关注笔者公众号一起学习交流,获取更多有用的知识~

自动化测试数据生成:Asp.Net Core单元测试利器AutoFixture详解的更多相关文章

  1. 【转】.NET(C#):浅谈程序集清单资源和RESX资源 关于单元测试的思考--Asp.Net Core单元测试最佳实践 封装自己的dapper lambda扩展-设计篇 编写自己的dapper lambda扩展-使用篇 正确理解CAP定理 Quartz.NET的使用(附源码) 整理自己的.net工具库 GC的前世与今生 Visual Studio Package 插件开发之自动生

    [转].NET(C#):浅谈程序集清单资源和RESX资源   目录 程序集清单资源 RESX资源文件 使用ResourceReader和ResourceSet解析二进制资源文件 使用ResourceM ...

  2. Asp.Net Core 单元测试正确姿势

    背景 ASP.NET Core 支持依赖关系注入 (DI) 软件设计模式,并且默认注入了很多服务,具体可以参考 官方文档, 相信只要使用过依赖注入框架的同学,都会对此有不同深入的理解,在此无需赘言. ...

  3. Asp.net中web.config配置文件详解(一)

    本文摘自Asp.net中web.config配置文件详解 web.config是一个XML文件,用来储存Asp.NET Web应用程序的配置信息,包括数据库连接字符.身份安全验证等,可以出现在Asp. ...

  4. asp.net iis URLRewrite 实现方法详解

    原文 asp.net iis URLRewrite 实现方法详解 实现非常简单首先你要在你的项目里引用两个dll:actionlessform.dll.urlrewriter.dll,真正实现重写的是 ...

  5. scribefire 多博客管理利器 安装详解

    scribefire 多博客管理利器 安装详解 一.ScribeFire介绍 ScribeFire 是 Firefox (火狐浏览器)上著名的博客写作工具,目前已跨平台支持多浏览器(Firefox,C ...

  6. 使用 Swagger 自动生成 ASP.NET Core Web API 的文档、在线帮助测试文档(ASP.NET Core Web API 自动生成文档)

    对于开发人员来说,构建一个消费应用程序时去了解各种各样的 API 是一个巨大的挑战.在你的 Web API 项目中使用 Swagger 的 .NET Core 封装 Swashbuckle 可以帮助你 ...

  7. 利用T4模板生成ASP.NET Core控制器的构造函数和参数

    前言 在ASP.NET Core中引入了DI,并且通过构造函数注入参数,控制器中会大量使用DI注入各种的配置参数,如果配置注入的参数比较多,而且各个控制器需要的配置参数都基本一样的话,那么不断重复的复 ...

  8. 关于单元测试的思考--Asp.Net Core单元测试最佳实践

    在我们码字过程中,单元测试是必不可少的.但在从业过程中,很多开发者却对单元测试望而却步.有些时候并不是不想写,而是常常会碰到下面这些问题,让开发者放下了码字的脚步: 这个类初始数据太麻烦,你看:new ...

  9. 使用 xUnit 编写 ASP.NET Core 单元测试

    还记得 .NET Framework 的 ASP.NET WebForm 吗?那个年代如果要在 Web 层做单元测试简直就是灾难啊..NET Core 吸取教训,在设计上考虑到了可测试性,就连 ASP ...

  10. GitHub自动化部署(CD) asp.net core 5.0 项目(免费空间)

    这里我简单介绍一下使用Github自动化部署自己项目到Heroku云服务器上,Heroku竟然是一个很非常老牌的云平台服务商,竟然还没听说过,网上一查2010被Salesforce收购,网上有很多关于 ...

随机推荐

  1. nginx进阶-3(32-34天)学习笔记

    nginx进阶-3(33-34天)学习笔记 知识回顾 1. nginx部署单机网站 2.nginx部署多个网站 3.nginx访问方式 4.nginx 安全 5.nginx加密访问 实战 00---n ...

  2. 【FastDFS】面试官:如何实现文件的大规模分布式存储?(全程实战)

    写在前面 在<[FastDFS]小伙伴们说在CentOS 8服务器上搭建FastDFS环境总报错?>一文中,详细的介绍了如何在CentOS 8服务器行搭建FastDFS环境.在生产环境中, ...

  3. Oracle 字符串拆分成一个一个字符

    SELECT (REGEXP_SUBSTR('LW112190', '[A-Z0-9]', 1, ROWNUM)) test FROM DUAL CONNECT BY ROWNUM <= LEN ...

  4. verilog之基本结构

    verilog语法的基本结构 1.verilog的定义 verilog,一种硬件描述语言,致力于提高数字电路,尤其是大规模数字电路的描述规范.从描述就可以看出,这个语言和C不同,不是高级语言.但是,这 ...

  5. Chrome浏览器安装离线插件Markdown Here

    前言 近期刚开通了微信公众号,想在上面发表一些自己的文章,由于排版问题很浪费时间,公众号后台本身不支持Markdown语法,所以就需要利用Markdown Here插件使得公众号后台支持Markdow ...

  6. Java,substring( )方法

    该方法可以获取一个String字符串的(x,y)个字符. 其中x和y是左闭右开的,左边的可以取到,右边的取不到,并且索引从0开始. 例如 1 String text = "reliableY ...

  7. OpenHarmony Docker移植实践

     Docker简介 从操作系统诞生之日起,虚拟化技术就不断的演进与发展,结合目前云原生的发展态势,容器无疑是其中的重要一环. Docker是一个开源的软件项目,可以在Linux操作系统上提供一层额外的 ...

  8. 熊磊:成功移植 OpenHarmony 到多套开发板,是最开心的事

    编者按:在 OpenHarmony 生态发展过程中,涌现了大批优秀的代码贡献者,本专题旨在表彰贡献.分享经验,文中内容来自嘉宾访谈,不代表 OpenHarmony 工作委员会观点. 熊磊 华为技术有限 ...

  9. innoSetup打包文件编写模板

    现在打包主要是使用 innosetup 这个软件来进行打包,支持录制脚本和手动编写脚本,比较好用. 此文章主要记录手写脚本,便于后期查询,借鉴. 文档: inno setup :https://blo ...

  10. openGauss/MogDB配置IPv6

    openGauss/MogDB 配置 IPv6 openGauss/MogDB 支持多种网络接口,假如我们想在支持 IPv6 的网络上部署使用,只需简单操作即可,本文将介绍在 Centos 上如何配置 ...