Search code examples
c#.netunit-testingxunitautofixture

XUnit and AutoFixture


Is it possible to configure AutoFixture so that for a certain property (e.g. Name) a value is chosen from a list?

F.e.

public class Person
{
   public string FirstName { get; set; }
}

In my test:

[Theory, AutoData]
public void Test(Person person)
{
   // person.FirstName can only be one of these names: "Marc, Jules, Tom"
}

I want to limit the possible values of a string property, is this possible? It's comparable to the Bogus PickRandom()...

Maybe by inheriting from the AutoDataAttribute?


Solution

  • Yep. There is a similar feature implemented in AutoFixture to generate domain names.

    You can find the adopted version of the DomainNameGenerator which selects person names from the predefined list:

    public class PersonNameGenerator : ISpecimenBuilder
    {
        private readonly ElementsBuilder<string> _nameBuilder = 
            new ElementsBuilder<string>("Marc", "Jules", "Tom");
    
        public object Create(object request, ISpecimenContext context)
        {
            if (context == null) throw new ArgumentNullException(nameof(context));
    
            if (request == null || !typeof(Person).Equals(request))
                return new NoSpecimen();
    
            var firstName = this._nameBuilder.Create(typeof(string), context) as string;
            if (firstName == null)
                return new NoSpecimen();
    
            return new Person { FirstName = firstName };
        }
    }
    

    Now the new generator needs to be registered:

    public class MyAutoDataAttribute : AutoDataAttribute
    {
        public MyAutoDataAttribute()
            : base(() =>
            {
                var fixture = new Fixture();
                fixture.Customizations.Insert(0, new PersonNameGenerator());
                return fixture;
            })
        {
        }
    }
    

    The name generator in action:

    [Theory, MyAutoData]
    public void Test(Person person)
    {
        Console.Out.WriteLine("person.FirstName = {0}", person.FirstName);
        // writes:
        // person.FirstName = Tom
        // person.FirstName = Jules
        // person.FirstName = Marc
    }
    

    Please note that this implementation will not generate other props (e.g. LastName) if any. More advanced version might be needed but this one is ok to get started.