0


.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按需生成有效的测试数据呢?方法其实有好几种:

一、直接定制

var fixture = new Fixture();
fixture.Customize<User>(c => c
    .With(x => x.Email, "特定值")
    .Without(x => x.Id));

这里,With方法用于指定属性的具体值,而Without方法用于排除某些属性不被自动填充。

二、使用匿名函数

这在需要对生成的数据进行更复杂的操作时非常有用。

var fixture = new Fixture();
fixture.Customize<User>(c => c.FromFactory(() => new User
{
    Email = "通过工厂方法生成",
}));

三、实现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());

四、使用

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

,那么推荐你尝试一下。

标签: 单元测试

本文转载自: https://blog.csdn.net/u014316335/article/details/140550894
版权归原作者 红薯不甜 所有, 如有侵权,请联系我们删除。

“.NET单元测试使用AutoFixture按需填充的方法总结”的评论:

还没有评论