自动化测试数据生成:Asp.Net Core单元测试利器AutoFixture详解
引言
在我们之前的文章中介绍过使用Bogus生成模拟测试数据,今天来讲解一下功能更加强大自动生成测试数据的工具的库"AutoFixture"。
什么是AutoFixture?
AutoFixture是一个针对.NET的开源库,旨在最大程度地减少单元测试中的“安排(Arrange)”阶段,以提高可维护性。它的主要目标是让开发人员专注于被测试的内容,而不是如何设置测试场景,通过更容易地创建包含测试数据的对象图,从而实现这一目标。
AutoFixture可以帮助开发人员自动生成测试数据,减少手动设置测试数据的工作量,提高单元测试的效率和可维护性。通过自动生成对象,开发人员可以更专注于编写测试逻辑,而不必花费大量精力在准备测试数据上。
其实和Bogus相比,AutoFixture更强大的地方在于可以自动化设置对象的值,当类发生变化时如属性名或者类型更改,我们不需要去进行维护,AutoFixture可以自动适应Class的变化。
AutoFixture与流行的 .NET 测试框架(如 NUnit 和 xUnit)可以无缝集成。
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.Api和Sample.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包括基本类型(如string、int)、自定义对象等
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);
}
修改后的例子中,我们使用 AutoFixture 的 Create<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);
}
自定义对象属性值
AutoFixture 的 Build 方法结合 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详解的更多相关文章
- 【转】.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 ...
- Asp.Net Core 单元测试正确姿势
背景 ASP.NET Core 支持依赖关系注入 (DI) 软件设计模式,并且默认注入了很多服务,具体可以参考 官方文档, 相信只要使用过依赖注入框架的同学,都会对此有不同深入的理解,在此无需赘言. ...
- Asp.net中web.config配置文件详解(一)
本文摘自Asp.net中web.config配置文件详解 web.config是一个XML文件,用来储存Asp.NET Web应用程序的配置信息,包括数据库连接字符.身份安全验证等,可以出现在Asp. ...
- asp.net iis URLRewrite 实现方法详解
原文 asp.net iis URLRewrite 实现方法详解 实现非常简单首先你要在你的项目里引用两个dll:actionlessform.dll.urlrewriter.dll,真正实现重写的是 ...
- scribefire 多博客管理利器 安装详解
scribefire 多博客管理利器 安装详解 一.ScribeFire介绍 ScribeFire 是 Firefox (火狐浏览器)上著名的博客写作工具,目前已跨平台支持多浏览器(Firefox,C ...
- 使用 Swagger 自动生成 ASP.NET Core Web API 的文档、在线帮助测试文档(ASP.NET Core Web API 自动生成文档)
对于开发人员来说,构建一个消费应用程序时去了解各种各样的 API 是一个巨大的挑战.在你的 Web API 项目中使用 Swagger 的 .NET Core 封装 Swashbuckle 可以帮助你 ...
- 利用T4模板生成ASP.NET Core控制器的构造函数和参数
前言 在ASP.NET Core中引入了DI,并且通过构造函数注入参数,控制器中会大量使用DI注入各种的配置参数,如果配置注入的参数比较多,而且各个控制器需要的配置参数都基本一样的话,那么不断重复的复 ...
- 关于单元测试的思考--Asp.Net Core单元测试最佳实践
在我们码字过程中,单元测试是必不可少的.但在从业过程中,很多开发者却对单元测试望而却步.有些时候并不是不想写,而是常常会碰到下面这些问题,让开发者放下了码字的脚步: 这个类初始数据太麻烦,你看:new ...
- 使用 xUnit 编写 ASP.NET Core 单元测试
还记得 .NET Framework 的 ASP.NET WebForm 吗?那个年代如果要在 Web 层做单元测试简直就是灾难啊..NET Core 吸取教训,在设计上考虑到了可测试性,就连 ASP ...
- GitHub自动化部署(CD) asp.net core 5.0 项目(免费空间)
这里我简单介绍一下使用Github自动化部署自己项目到Heroku云服务器上,Heroku竟然是一个很非常老牌的云平台服务商,竟然还没听说过,网上一查2010被Salesforce收购,网上有很多关于 ...
随机推荐
- C# 单例模式使用 Singleton
Singleton 类如下: public class Singleton<T> where T : class, new() { private static T _instance; ...
- C++设计模式 - 解析器模式(Interpreter)
领域规则模式 在特定领域中,某些变化虽然频繁,但可以抽象为某种规则.这时候,结合特定领域,将问题抽象为语法规则,从而给出在该领域下的一般性解决方案. 典型模式 Interpreter Interpre ...
- #正余弦定理#牛客练习赛71 B 烙印
题目 将三角形的六要素只留下三个已知条件, 问有多少种情况,多组询问 分析 首先分类讨论一下(对新高一不友好,比如说我). 前置知识: 正弦定理: \[\frac{a}{sinA}=\frac{b}{ ...
- Qt调用摄像头二,Pro版
本示例,为纯Qt调用摄像头,功能会比版本一要多一点:打开摄像头,设置参数,完整拍照,框选拍照,切换分辨率,旋转,水平镜像,垂直镜像,放大,缩小 上一个版本,使用的显示窗口直接显示出摄像头画面,所以可操 ...
- openGauss2.1.0在openEuler 20.03 LTS SP2 安装后,yum无法使用的问题解决
openGauss2.1.0 在 openEuler 20.03 LTS SP2 安装后,yum 无法使用的问题解决 一.环境描述 操作系统: openEuler 20.03 LTS openEule ...
- HarmonyOS应用开发Web组件基本属性应用和事件
一.Web组件概述 Web组件用于在应用程序中显示Web页面内容,为开发者提供页面加载.页面交互.页面调试等能力. ● 页面加载:Web组件提供基础的前端页面加载的能力,包括加载网络页面.本地页面 ...
- 报表输入页码翻页(润乾 V2018)
报表数据分了太多页,一页一页翻页查看数据嫌麻烦,可以试试这种翻页效果--输入页码翻页. 润乾报表提供了翻页相关的 JS 函数,可以在报表展现的页面中添加 JS 调用翻页函数实现输入页码跳转到对应页. ...
- 重新点亮shell————awk函数[十五]
前言 简单介绍一下awk函数. 正文 算术函数 字符串函数 自定义函数 例子: 结 awk就到这里了.
- js es6系列——map函数
正文 map,必要解释就是map不是地图的意思,而是映射的意思. 这里就简单的介绍了这个map了. array.map(callback,[ thisObject]); 看下这个案例后,我们发现了就发 ...
- 分享一款嵌入式开源按键框架代码工程MultiButton
一.工程简介 MultiButton 是一个小巧简单易用的事件驱动型按键驱动模块. Github地址:https://github.com/0x1abin/MultiButton 这个项目非常精简,只 ...