.NET单元测试使用AutoFixture按需填充属性的几种方式,以及最佳实践
AutoFixture是一个.NET库,旨在简化单元测试中的数据设置过程。通过自动生成测试数据,它帮助开发者减少测试代码的编写量,使得单元测试更加简洁、易读和易维护。AutoFixture可以用于任何.NET测试框架,如xUnit、NUnit或MSTest。
默认情况下AutoFixture生成的字段值很多时候都满足不了测试需求,比如:
public class User
{
public int Id { get; set; }
public string Name { get; set; } = null!;
[EmailAddress]
public string? Email { get; set; }
[StringLength(512)]
public string? Address { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.Now;
}
如果直接使用 Create<T>()生成的User对象,他会默认给你填充Id为随机整数,Name和Email为一串Guid,显然这里的邮箱地址生成就不能满足要求,并不是一个有效的邮箱格式
那么如何让AutoFixture按需生成有效的测试数据呢?方法其实有好几种:
- 方法1:直接定制
var fixture = new Fixture();
fixture.Customize<User>(c => c
.With(x => x.Email, "特定值")
.Without(x => x.Id));
这里,With方法用于指定属性的具体值,而Without方法用于排除某些属性不被自动填充。
- 方法2:使用匿名函数
这在需要对生成的数据进行更复杂的操作时非常有用。
var fixture = new Fixture();
fixture.Customize<User>(c => c.FromFactory(() => new User
{
Email = "通过工厂方法生成",
}));
- 方法3:实现ICustomization接口
对于更复杂的定制需求,可以通过实现ICustomization接口来创建一个定制化类。这种方法的好处是可以重用定制逻辑,并且使得测试代码更加整洁。
public class MyCustomClassCustomization : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customize<User>(c => c
.With(x => x.Email, "自定义值")
.Without(x => x.Id));
}
}
// 使用定制化
var fixture = new Fixture();
fixture.Customize(new MyCustomClassCustomization());
- 方法4:使用
Build<T>方法
Build<T>方法提供了一种链式调用的方式来定制类型的生成规则,这在只需要对单个对象进行简单定制时非常方便。
var myCustomObject = fixture.Build<User>()
.With(x => x.Email, $"{Guid.NewId()}@example.com")
.Without(x => x.Id)
.Create();
最佳实践:
这里以xunit测试框架为例,
我们需要提前引用AutoFixture,AutoFixture.Xunit2库,实现一个UserAutoDataAttribute类,继承自InlineAutoDataAttribute 重写GetData方法,大致代码如下:
public class UserAutoDataAttribute : InlineAutoDataAttribute
{
public UserAutoDataAttribute(params object[] values) : base(values)
{
ArgumentNullException.ThrowIfNull(values[0]);
}
public override IEnumerable<object[]> GetData(MethodInfo testMethod)
{
var fixture = new Fixture();
//这里使用上面的4种方式的一种,亦或者根据自身情况定制!
var user = fixture.Build<User>()
//.With(x => x.Id, 0)
.Without(x => x.Id) //ID需要排除因为EFCore需要插入时自动生成
.With(x => x.Email, $"{Uuid7.NewUuid7()}@example.com") //邮箱地址,需要照规则生成
.Create();
yield return new object[] { Values[0], user };
}
}
下面是一个测试用例,需要填充db,和一个自动生成的User参数
public class UnitOfWorkTests(ITestOutputHelper output)
{
[Theory]
[UserAutoData(1)]
[UserAutoData(2)]
public async Task MyUnitOfWorkTest(int db, User user)
{
var services = new ServiceCollection();
services.AddLogging();
services.AddDbContext<TestDbContext>(options =>
{
options.UseInMemoryDatabase($"test-{db}");
});
services.AddUnitOfWork<TestDbContext>();
var provider = services.BuildServiceProvider();
var uow = provider.GetRequiredService<IUnitOfWork<TestDbContext>>();
//add user
await uow.GetRepository<User>().InsertAsync(user);
await uow.SaveChangesAsync();
// select user
var user2 = await uow.GetRepository<User>().FindAsync(1);
Assert.NotNull(user2);
// delete user
uow.GetRepository<User>().Delete(1);
var row = await uow.SaveChangesAsync();
Assert.Equal(1, row);
// select user
user2 = await uow.GetRepository<User>().GetFirstOrDefaultAsync(x => x.Id == 1);
Assert.Null(user2);
}
}
如果你已经习惯编写单元测试,但还没有使用AutoFixture,那么推荐你尝试一下,也许你也会喜欢上TA
.NET单元测试使用AutoFixture按需填充属性的几种方式,以及最佳实践的更多相关文章
- spring配置属性的两种方式
spring配置属性有两种方式,第一种方式通过context命名空间中的property-placeholder标签 <context:property-placeholder location ...
- MVC3+EF4.1学习系列(五)----- EF查找导航属性的几种方式
文章索引和简介 通过上一篇的学习 我们把demo的各种关系终于搭建里起来 以及处理好了如何映射到数据库等问题 但是 只是搭建好了关系 问题还远没有解决 这篇就来写如何查找导航属性 和查找导航属性的几种 ...
- route按需加载的3种方式:vue异步组件、es提案的import()、webpack的require.ensure()
1. vue异步组件技术 vue-router配置路由,使用vue的异步组件技术,可以实现按需加载. 但是,这种情况下一个组件生成一个js文件.举例如下: { path: '/promisedemo' ...
- Spring学习(五)-----注入bean属性的三种方式( 1: 正常的方式 2: 快捷方式 3: “p” 模式)
在Spring中,有三种方式注入值到 bean 属性. 正常的方式 快捷方式 “p” 模式 看到一个简单的Java类,它包含两个属性 - name 和 type.稍后将使用Spring注入值到这个 b ...
- 原生js动态创建、获取、删除属性的几种方式
1.创建属性 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <ti ...
- android 自定义控件中获取属性的三种方式(转)
第一种方法,直接设置属性值,通过attrs.getAttributeResourceValue拿到这个属性值. (1)在xml文件中设置属性值 <com.example.activity.Ico ...
- javascript创建对象和属性的几种方式
一句话,javascript里面的对象,即是函数.方法. (一)第一种: a.声明对象:var JHSoft = JHSoft || {}; 或者 var JHSoft=new Object(); b ...
- Dynamics CRM2013/2015 检索实体属性的两种方式
昨天有朋友问起如何查询一个字段属性是否存在于某个实体中,一般这个问题我们会采取最直观的查询方式即MetadataBrowser,该工具是一个zip解决方案包在SDK中的如下目录内"\SDK\ ...
- vue中路由按需加载的几种方式
使用vue-cli构建项目后,我们会在Router文件夹下面的index.js里面引入相关的路由组件,如: import Hello from '@/components/Hello' import ...
- [转]Spring通过@Value注解注入属性的几种方式
原文地址:https://blog.csdn.net/csujiangyu/article/details/50945486 ------------------------------------- ...
随机推荐
- PasteSpider之接口的授权实现为什么不采用JWT方式
PasteTemplate序列的接口权限控制使用的都是一套逻辑 包括不限于PasteSpider,PasteTimer,PasteTicker等 大致逻辑一致,具体的细节可能会根据项目做一些调整! 实 ...
- 用Vue全家桶纯手工搓了一个开源版「抖音」
前言 2018年刚入行前端时,公司使用的还是Angular.Angular什么都好,就是写代码时的体验老糟心了,改一个地方,按下保存之后,要等好几秒刷新后才能看到效果,Webstorm无比好用的自动保 ...
- nginx002: NGINX的环境变量,基本命令参数,目录关系及gzip优化
NGINX的环境变量,基本命令参数,目录关系及gzip优化 添加环境变量 nginx命令执行方式 nginx [选项] [参数] 通过编译安装的nginx 默认是没有环境变量的,如何设置环境变量,让n ...
- MySQL学习笔记-多表查询(上)
多表查询(上) 一. 多表关系 在实际应用中,根据需求,设计的表结构之间存在联系,联系一般分为以下三种 一对多(多对一) 多对多 一对一 1. 一对多(多对一) 案例:部门与员工的关系,一个部门对应多 ...
- ztree.js 禁止点击事件和鼠标禁用
先看样式 var _t = this; var setting = { view: { fontCss: { color: "#5E5F61" }, showIcon: true, ...
- 字符型 ASCLL编码 转义字符
字符(character) char 2字节 每一个字符的背后.都有一个数字做代表(对照,参照的表) 字符赋值 char cl = 'a';通过''单引号描述为字符赋值 整数赋值 char c2 ...
- ETL工具-nifi干货系列 第九讲 处理器EvaluateJsonPath,根据JsonPath提取字段
1.其实这一节课本来按照计划一起学习RouteOnAttribute处理器(相当于java中的ifelse,switch case 控制语句),但是在学习的过程中遇到了一些问题.RouteOnAttr ...
- 使用Logstash同步Mysql到Easysearch
从 MySQL 同步数据到 ES 有多种方案,这次我们使用 ELK 技术栈中的 Logstash 来将数据从 MySQL 同步到 Easysearch . 方案前提 MySQL 表记录必须有主键,比如 ...
- 使用 INFINI Console 实现 Elasticsearch 的增量数据迁移
功能介绍 # 在 INFINI Console 1.3.0 版本里,数据迁移功能增加了对增量迁移的支持.这篇文章将会介绍增量迁移的具体使用方法和实现原理. 场景介绍 # 以常见的日志场景为例,假设 A ...
- reids分片技术cluster篇
为什么学redis-cluster 前面两篇文章,主从复制和哨兵机制保障了高可用 就读写分离,而言虽然slave节点扩展了主从的读并发能力 但是写能力和存储能力是无法进行扩展,就只能是master节点 ...