【问题标题】:AutoFixture build collection with unique property具有独特属性的 AutoFixture 构建集合
【发布时间】:2019-03-26 15:42:36
【问题描述】:

有没有可能在AutoFixture 中创建具有唯一属性的集合?例如,我想创建一个集合:

public class Foo {
 public int Id {get; set;}
 public string Name {get;set;}
}

具有独特的Id。它看起来像这样:

var fixture = new Fixture();

fixture
 .Build<Foo>()
 .WithUnique((foo) => foo.Id)
 .CreateMany(20);

我知道可以通过自定义来做到这一点,但我认为这是很常见的情况,所以AutoFixture 可能已经为此做好了准备?

【问题讨论】:

    标签: c# .net autofixture


    【解决方案1】:

    默认情况下,Autofixture 会为属性生成唯一值。因此,您不必指定哪个属性应该是唯一的 - 而是为其他属性指定一个非唯一值:

    // with AutoFixture.SeedExtensions
    fixture.Build<Foo>().With(f => f.Name, fixture.Create("Name")).CreateMany(20)
    

    请注意,如果您想确保其他属性的值不唯一(只有 Id 唯一),那么您可以为 IPostprocessComposer 创建简单的扩展,为该属性提供一组可能的值:

    public static IPostprocessComposer<T> With<T, TProperty>(
        this IPostprocessComposer<T> composer,
        Expression<Func<T, TProperty>> propertyPicker,
        IEnumerable<TProperty> possibleValues) =>
          composer.With(propertyPicker, possibleValues.ToArray());
    
    public static IPostprocessComposer<T> With<T, TProperty>(
        this IPostprocessComposer<T> composer,
        Expression<Func<T, TProperty>> propertyPicker,
        params TProperty[] possibleValues)
    {
        var rnd = new Random();
        return composer.With(
           propertyPicker,
           () => possibleValues[rnd.Next(0, possibleValues.Length)]);
    }
    

    用法很简单 - 下面的代码创建 foos 列表,其中只有两个不同的 name 值,以及三个不同的整数属性值:

    fixture.Build<Foo>()
        .With(f => f.SomeIntegerProperty, 10, 20, 50)
        .With(f => f.Name, fixture.CreateMany<string>(2))
        .CreateMany(20);
    

    【讨论】:

    • 所以它始终是唯一的,除非您在集合中超过 byte 类型的 255 个元素?
    • @OlegI 是的,有 RandomNumericSequencegenerator 使用它创建从 1 开始的随机、唯一的数字序列。(它只跟踪范围内的随机数列表已经返回)。返回范围内的所有数字后,清除历史记录并从头开始生成
    • @OlegI 你可以查看号码生成here
    • @SergeyBerezovskiy 实际上,在超出byte 范围后,RandomNumericSequenceGenerator 会切换到更大的范围,其中不包括之前的范围。因此,如果您的属性足够宽(例如shortint),我会说数字始终是唯一的。