如何配置AutoFixture以在创建许多特定类型时使用枚举值作为种子

本文关键字:类型 枚举 种子 许多特 配置 何配置 AutoFixture 创建 | 更新日期: 2023-09-27 18:18:21

我有以下类型:

public enum Status
{
    Online,
    Offline
}
public class User
{
    private readonly Status _status;
    public User(Status status) { _status = status; }
    public Status Status {get {return _status; }}
    public string Name {get;set;}
}

现在,当执行fixture.CreateMany<User>时,我希望AutoFixture返回两个Users,每个状态一个。所有其他属性——比如Name——应该用匿名数据填充。

问题:
如何配置自动夹具来做到这一点?


我试了下面这个:

  1. 注册更新User对象的集合:

    fixture.Register(
        () => Enum.GetValues(typeof(Status)).Cast<Status>().Select(s => 
            new User(s)));
    

    这种方法的问题是AutoFixture不填充其他属性,如Name

  2. 自定义User使用工厂并注册一个使用fixture.Create的集合:

        f.Customize<User>(c => c.FromFactory((Status s) => new User(s)));
        f.Register(() =>
            Enum.GetValues(typeof(Status))
                .Cast<Status>()
                .Select(s => (User)f.Create(new SeededRequest(typeof(User), s),
                                            new SpecimenContext(f))));
    

    那也没用。

如何配置AutoFixture以在创建许多特定类型时使用枚举值作为种子

你可以这样做:

var users = new Fixture().Create<Generator<User>>();
var onlineUser = users.Where(u => u.Status == Status.Online).First();
var offlineUser = users.Where(u => u.Status == Status.Offline).First();

如果你正在使用AutoFixture。在Xunit中,声明式等价的是:

[Theory, AutoData]
public void CreateOneOfEachDeclaratively(Generator<User> users)
{
    var onlineUser = users.Where(u => u.Status == Status.Online).First();
    var offlineUser = users.Where(u => u.Status == Status.Offline).First();
    // Use onlineUser and offlineUser here...
}

您可以声明并使用自定义,例如StatusGenerator:

var fixture = new Fixture();
fixture.RepeatCount = 2;
fixture.Customizations.Add(new StatusGenerator());
var result = fixture.CreateMany<User>();

一个假设的 StatusGenerator的实现可能如下:

internal class StatusGenerator : ISpecimenBuilder
{
    private readonly Status[] values;
    private int i;
    internal StatusGenerator()
    {
        this.values =
            Enum.GetValues(typeof(Status)).Cast<Status>().ToArray();
    }
    public object Create(object request, ISpecimenContext context)
    {
        var pi = request as ParameterInfo;
        if (pi == null || !pi.ParameterType.IsEnum)
            return new NoSpecimen(request);
        return this.values[i == this.values.Length - 1 ? i = 0 : ++i];
    }
}

根据Mark的回答,我现在使用的是:

fixture.Customize<User>(c => c.Without(x => x.Status));
fixture.Customize<IEnumerable<User>>(
    c =>
    c.FromFactory(
        () => Enum.GetValues(typeof(Status)).Cast<Status>()
                  .Select(s => users.First(u => u.Status == s))));
fixture.Create<IEnumerable<User>>(); // returns two Users

我知道它已经得到了回答,Generator是一个非常有趣的发现。我认为有一个更简单的方法来解决这个问题。

        var numberOfEnumValues = Enum.GetValues(typeof(Status)).Length;
        var users = fixture.CreateMany<User>(numberOfEnumValues);

如果构造函数更复杂,有多个Status值,或者模型有Status类型的属性设置器。那么你通常会遇到问题,发电机也可能会爆炸。

说:

    public class SuperUser : User
    {
        public SuperUser(Status status, Status shownStatus): base(status)
        {
        }
    }

那么这个将永远不会被求值:

    var users = fixture.Create<Generator<SuperUser>>();
    var offlineUser = users.Where(u => u.Status == Status.Offline).First();

AutoFixture 4.17.0的当前操作方式

fixture
.Build<User>()
.With(u => u.Status, Status.Offline)
.CreateMany(5)
.ToList();