IT评测·应用市场-qidao123.com技术社区

标题: 自动化测试数据天生:Asp.Net Core单位测试利器AutoFixture详解 [打印本页]

作者: 河曲智叟    时间: 2024-5-18 18:00
标题: 自动化测试数据天生:Asp.Net Core单位测试利器AutoFixture详解
引言

在我们之前的文章中先容过使用Bogus天生模仿测试数据,今天来解说一下功能更加强盛自动天生测试数据的工具的库"AutoFixture"。
什么是AutoFixture?

AutoFixture 是一个针对 .NET 的开源库,旨在最大程度地减少单位测试中的“安排(Arrange)”阶段,以提高可维护性。它的主要目标是让开发职员专注于被测试的内容,而不是怎样设置测试场景,通过更容易地创建包含测试数据的对象图,从而实现这一目标。
AutoFixture 可以帮助开发职员自动天生测试数据,减少手动设置测试数据的工作量,提高单位测试的效率和可维护性。通过自动天生对象,开发职员可以更专注于编写测试逻辑,而不必耗费大量精力在准备测试数据上。
其实和Bogus相比,AutoFixture更强盛的地方在于可以自动化设置对象的值,当类发生变化时如属性名或者类型更改,我们不须要去举行维护,AutoFixture可以自动顺应Class的变化。
AutoFixture与盛行的 .NET 测试框架(如 NUnit 和 xUnit)可以无缝集成。
AutoFixture实战

我们在创建xUnit单位测试项目dotNetParadise.AutoFixture
安装依赖

创建完项目之后我们首先要安装Nuget包
  1. PM> NuGet\Install-Package AutoFixture -Version 4.18.1
复制代码
初始化

AutoFixture的使用是从一个Fixture的实例对象开始的
  1. var fixture = new Fixture();
复制代码
接下来我们先创建一个测试类来学一下AutoFixture的使用
  1. public class AutoFixtureStaffTest
  2. {
  3.     private readonly IFixture _fixture;
  4.     public AutoFixtureStaffTest()
  5.     {
  6.         _fixture = new Fixture();
  7.     }
  8. }
复制代码
实战

我们之前的测试项目创建了Sample.Api和Sample.Repository两个类库来做我们被测试的项目,本章继续使用Sample.Repository来演示AutoFixture的使用。
dotNetParadise.AutoFixture 测试项目添加Sample.Repository的项目引用
在 Sample.Repository中我们有一个Staff的实体对象,继续用作我们的测试
  1. public class Staff
  2. {
  3.     public int Id { get; set; }
  4.     public string Name { get; set; }
  5.     public string Email { get; set; }
  6.     public int? Age { get; set; }
  7.     public List<string>? Addresses { get; set; }
  8.     public DateTimeOffset? Created { get; set; }
  9.     public void Update(Staff staff)
  10.     {
  11.         this.Name = staff.Name;
  12.         this.Email = staff.Email;
  13.         this.Age = staff.Age;
  14.         this.Addresses = staff.Addresses;
  15.         Created = staff.Created;
  16.     }
  17. }
复制代码
属性赋值
  1.    [Fact]
  2.    public void Staff_SetProperties_ValuesAssignedCorrectly()
  3.    {
  4.        //Arrange
  5.        Staff staff = new Staff();
  6.        //生成Int类型
  7.        staff.Id = _fixture.Create<int>();
  8.        //生成string 类型
  9.        staff.Name = _fixture.Create<string>();
  10.        //生成DateTimeOffset类型
  11.        staff.Created = _fixture.Create<DateTimeOffset>();
  12.        //生成 List<string>?
  13.        staff.Addresses = _fixture.CreateMany<string>(Random.Shared.Next(1, 100)).ToList();
  14.        //Act
  15.        //...省略
  16.        // Assert
  17.        Assert.NotNull(staff); // 验证 staff 对象不为 null
  18.        // 验证 staff.Id 是 int 类型
  19.        Assert.IsType<int>(staff.Id);
  20.        // 验证 staff.Name 是 string 类型
  21.        Assert.IsType<string>(staff.Name);
  22.        // 验证 staff.Created 是 DateTimeOffset? 类型
  23.        Assert.IsType<DateTimeOffset>(staff.Created);
  24.        // 验证 staff.Addresses 是 List<string> 类型
  25.        Assert.IsType<List<string>>(staff.Addresses);
  26.        // 验证 staff.Addresses 不为 null
  27.        Assert.NotNull(staff.Addresses);
  28.        // 验证 staff.Addresses 中的元素数量在 1 到 100 之间
  29.        Assert.InRange(staff.Addresses.Count, 1, 100);
  30.    }
复制代码
示例中用到 AutoFixture 提供的的方法随机分配随机值,上面的示例中用到使用到了两个方法
Create方法
CreateMany方法
T包括根本类型(如 string、int)、自定义对象等
Create构造对象

上面的例子我们本身实例化的对象然后对对象挨个赋值,目的是让大家对AutoFixture的使用有一个初步的认识,上面也表明到了Create的泛型参数T可以是自定义的对象,那么我们来简化一下上面的示例
  1. [Fact]
  2. public void Staff_ObjectCreation_ValuesAssignedCorrectly()
  3. {
  4.     // Arrange
  5.     Staff staff = _fixture.Create<Staff>(); // 使用 AutoFixture 直接创建 Staff 对象
  6.     // Act
  7.     //...省略
  8.     // Assert
  9.     Assert.NotNull(staff); // 验证 staff 对象不为 null
  10.     // 验证 staff.Id 是 int 类型
  11.     Assert.IsType<int>(staff.Id);
  12.     // 验证 staff.Name 是 string 类型
  13.     Assert.IsType<string>(staff.Name);
  14.     // 验证 staff.Created 是 DateTimeOffset? 类型
  15.     Assert.IsType<DateTimeOffset>(staff.Created);
  16.     // 验证 staff.Addresses 是 List<string> 类型
  17.     Assert.IsType<List<string>>(staff.Addresses);
  18.     // 验证 staff.Addresses 不为 null
  19.     Assert.NotNull(staff.Addresses);
  20.     // 验证 staff.Addresses 中的元素数量在 1 到 100 之间
  21.     Assert.InRange(staff.Addresses.Count, 1, 100);
  22. }
复制代码
修改后的例子中,我们使用 AutoFixture 的 Create() 方法直接创建了一个 Staff 对象,而不是手动为每个属性赋值。如许可以更简洁地天生对象实例。
数据驱动测试

在正常的同一个测试方法中使用不同的输入数据举行测试时,通常都是基于 Theory 属性配合InlineData或者MemberData来完成的,有了AutoFixture之后数据也不用我们本身造了,来看一下实战入门
第一步Nuget安装依赖
  1. PM> NuGet\Install-Package AutoFixture.Xunit2 -Version 4.18.1
复制代码
[AutoData]属性
  1. [Theory, AutoData]
  2. public void Staff_Constructor_InitializesPropertiesCorrectly(
  3.     int id, string name, string email, int? age, List<string> addresses, DateTimeOffset? created)
  4. {
  5.     // Act
  6.     var staff = new Staff { Id = id, Name = name, Email = email, Age = age, Addresses = addresses, Created = created };
  7.     // Assert
  8.     Assert.Equal(id, staff.Id);
  9.     Assert.Equal(name, staff.Name);
  10.     Assert.Equal(email, staff.Email);
  11.     Assert.Equal(age, staff.Age);
  12.     Assert.Equal(addresses, staff.Addresses);
  13.     Assert.Equal(created, staff.Created);
  14. }
复制代码
通过 AutoData 方法,测试方法的参数化设置变得更加简单和高效,使得编写参数化测试方法变得更加容易。
[InlineAutoData]属性

假如我们有须要提供的特定化参数,可以用[InlineAutoData]属性,详细使用可以参考如下案例
  1.     [Theory]
  2.     [InlineAutoData(1)]
  3.     [InlineAutoData(2)]
  4.     [InlineAutoData(3)]
  5.     [InlineAutoData]
  6.     public void Staff_ConstructorByInlineData_InitializesPropertiesCorrectly(
  7.      int id, string name, string email, int? age, List<string> addresses, DateTimeOffset? created)
  8.     {
  9.         // Act
  10.         var staff = new Staff { Id = id, Name = name, Email = email, Age = age, Addresses = addresses, Created = created };
  11.         // Assert
  12.         Assert.Equal(id, staff.Id);
  13.         Assert.Equal(name, staff.Name);
  14.         Assert.Equal(email, staff.Email);
  15.         Assert.Equal(age, staff.Age);
  16.         Assert.Equal(addresses, staff.Addresses);
  17.         Assert.Equal(created, staff.Created);
  18.     }
复制代码
自定义对象属性值

AutoFixture 的 Build 方法联合 With 方法可以用于自定义对象的属性值
  1.     [Fact]
  2.     public void Staff_SetCustomValue_ShouldCorrectly()
  3.     {
  4.         var staff = _fixture.Build<Staff>()
  5.             .With(_ => _.Name, "Ruipeng")
  6.             .Create();
  7.         Assert.Equal("Ruipeng", staff.Name);
  8.     }
复制代码
禁用属性自动天生

在 AutoFixture 中,可以使用 OmitAutoProperties 方法来关闭自动属性天生,从而避免自动天生属性值。这在须要手动设置所有属性值的情况下很有用。
  1.     [Fact]
  2.     public void Test_DisableAutoProperties()
  3.     {
  4.         // Arrange
  5.         var fixture = new Fixture();
  6.         var sut = fixture.Build<Staff>()
  7.                          .OmitAutoProperties()
  8.                          .Create();
  9.         // Assert
  10.         Assert.Equal(0, sut.Id); // 验证 Id 属性为默认值 0
  11.         Assert.Null(sut.Name); // 验证 Name 属性为 null
  12.         Assert.Null(sut.Email); // 验证 Email 属性为 null
  13.         Assert.Null(sut.Age); // 验证 Age 属性为 null
  14.         Assert.Null(sut.Addresses); // 验证 Addresses 属性为 null
  15.         Assert.Null(sut.Created); // 验证 Created 属性为 null
  16.     }
复制代码
Do 方法执行自定义操作

Do 方法是 AutoFixture 中用于执行操作的方法,通常联合 Build 方法一起使用,用于在构建对象时执行自定义操作。让我详细表明一下 Do 方法的用法和作用:
主要特点:
  1.    [Fact]
  2.    public void Test_UpdateMethod()
  3.    {
  4.        // Arrange
  5.        var fixture = new Fixture();
  6.        var staff1 = fixture.Create<Staff>();
  7.        var staff2 = fixture.Create<Staff>();
  8.        // 使用 Do 方法执行自定义操作
  9.        var staff3 = fixture.Build<Staff>()
  10.                                  .Do(x => staff1.Update(staff2))
  11.                                  .Create();
  12.        // Assert
  13.        Assert.Equal(staff2.Name, staff1.Name); // 验证 Name 是否更新
  14.        Assert.Equal(staff2.Email, staff1.Email); // 验证 Email 是否更新
  15.        Assert.Equal(staff2.Age, staff1.Age); // 验证 Age 是否更新
  16.        Assert.Equal(staff2.Addresses, staff1.Addresses); // 验证 Addresses 是否更新
  17.        Assert.Equal(staff2.Created, staff1.Created); // 验证 Created 是否更新
  18.    }
复制代码
创建三个对象,在第三个创建过程中把第一个的对象属性用第二个对象的属性覆盖。
Customize Type 自定义类型

使用自定义类型构建器来执行复杂的初始化,并且保证了创建多个相同的实例中要保持同等的自定义行为。
首先我们可以在我们的测试类构造函数中定义一个自定义规则
  1.     public AutoFixtureStaffTest()
  2.     {
  3.         _fixture = new Fixture();
  4.         _fixture.Customize<Staff>(composer => composer.With(x => x.Email, "zhangsan@163.com"));
  5.     }
复制代码
比如我设置了所有的 email 都叫zhangsan@163.com
  1.     [Fact]
  2.     public void Test_StaffNameIsJohnDoe()
  3.     {
  4.         // Arrange
  5.         Staff staff = _fixture.Create<Staff>();
  6.         // Act
  7.         // Assert
  8.         Assert.Equal("zhangsan@163.com", staff.Email);
  9.     }
复制代码
这个位置大概得头脑就是如许,保证用到的多实例都有相同的行为,可以参考:
使用 AutoFixture 自定义类型的天生器
Auto-Mocking with Moq

第一步安装Nuget包
  1. PM> NuGet\Install-Package AutoFixture.AutoMoq -Version 4.18.1
复制代码
  1.     [Fact]
  2.     public async Task Repository_Add_ShouleBeSuccess()
  3.     {
  4.         _fixture.Customize(new AutoMoqCustomization());
  5.         var repoMock = _fixture.Create<IStaffRepository>();
  6.         Assert.NotNull(repoMock);
  7.     }
复制代码
创建 Fixture 实例并使用 AutoMoqCustomization 举行定制化,以便自动模仿 Moq 对象。
使用 Create() 方法创建一个可分配给 IInterface 接口的模仿实例。
Auto-configured Mocks

官网示例:
  1. fixture.Customize(new AutoMoqCustomization { ConfigureMembers = true });
  2. fixture.Inject<int>(1234);
  3. var document = fixture.Create<IDocument>();
  4. Console.WriteLine(document.Id); // 1234
复制代码
当将 ConfigureMembers = true 添加到 AutoMoqCustomization 中时,不但会作为自动模仿容器,还会自动配置所有天生的模仿对象,使其成员返回 AutoFixture 天生的值。
使用 Inject(1234) 将整数值 1234注入到 Fixture 中。
使用 Create() 创建一个 IDocument 接口的实例,并输出其 Id 属性值。
更多

在 Moq 框架中存在一些限定,其中自动配置模式无法设置具有 ref参数的方法,并且也无法配置泛型方法。然而,您可以使用 ReturnsUsingFixture 扩展方法轻松地设置这些方法。
官网示例:
  1. converter.Setup(x => x.Convert<double>("10.0"))
  2.          .ReturnsUsingFixture(fixture);
复制代码
在这个示例中,使用 ReturnsUsingFixture 扩展方法手动设置了一个名为 Convert 的泛型方法的行为。
当调用 Convert  方法并传入字符串"10.0" 时,ReturnsUsingFixture方法将使用fixture天生的值作为返回值。 通过使用ReturnsUsingFixture扩展方法,您可以绕过Moq框架的限定,手动设置具有ref` 参数或泛型方法的行为,以满足特定的测试需求.
末了

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


<em>
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 IT评测·应用市场-qidao123.com技术社区 (https://dis.qidao123.com/) Powered by Discuz! X3.4