Search code examples
c#.netautofixture

AutoFixture build collection with unique property


Is there any possibility to create a collection with a unique property in AutoFixture? For instance, I would like to create a collection of:

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

with unique Id. It would look something like this:

var fixture = new Fixture();

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

I know it is possible to do this through customization, but I think it's quite common scenario so might be AutoFixture has something ready for this?


Solution

  • Autofixture by default generates unique values for properties. So you don't have to specify which property should be unique - instead, specify a non-unique value for other property:

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

    Note if you want to ensure non-unique values for other properties (having only Id unique), then you can create simple extensions for IPostprocessComposer which provide a set of possible values for the property:

    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)]);
    }
    

    Usage is simple - following code creates list of foos with only two different values for name, and three different values for some integer property:

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