Customizations for AutoFixture – My new best friend

I recently attended a course organized by Mark Seemann at my workplace. Among other topics, the course involved advanced usage of AutoFixture, a framework that I’ve loved using for a couple of years now. AutoFixture always did a good job of helping to create dummy data for my unit tests, but one thing always bugged me. In pretty much all my tests, I’d really want to write this:

var fixture = new Fixture();
var model = fixture.CreateAnonymous<MyModel>();

but always ended up writing something like this:

var fixture = new Fixture();
var model =
    fixture
        .Build<MyModel>()
        .Without(x => x.Id)
        .CreateAnonymous();

So what’s the difference between the two code samples? In the first sample, I simply tell AutoFixture to create a new instance of MyModel with generated values for all public properties. In the second sample, a new instance is created with all public properties with generated values besides the Id-property. The above sample is inspired by an integration test I’ve written of the backend for a couple of Windows Phone apps created together with Casper Skydt. The idea here is to generate a model containing random values, besides the Id-property, which is generated by an Embedded RavenDB on insert. Letting AutoFixture generate a random value for the Id-property makes RavenDB fail because I instructed RavenDB to assign primary keys.

I typically solved the above problem with private help-methods generating MyModel instances with AutoFixture, but the whole idea with the simplicity and easy of creating new objects with AutoFixture, sort of disappears when starting to create test helpers to create test-data.

Mark showed a nice feature of AutoFixture, which is actually able to fix problems like this (and a lot more): Customizations. In fact customizations for AutoFixture is implementing types of the ISpecimenBuilder interface. A specimen builder is sort of the backbone of AutoFixtures algorithms for generating random values for different types of properties. Lucklily the AutoFixture folks added the ability to add custom specimen builders. In my case, I want AutoFixture to generate a new instance of the MyModel type without an assigned value to the Id-property. In fact, I want AutoFixture to never assign values to properties named Id and of type string. This behavior can be implemented as a customization like this:

namespace ThomasArdal.SpecimenBuilders
{
    public class NoIdSpecimenBuilder : ISpecimenBuilder
    {
        public object Create(object request, ISpecimenContext context)
        {
            var propertyInfo = request as PropertyInfo;
            if (propertyInfo != null &&
                propertyInfo.Name.Equals("Id") && 
                propertyInfo.PropertyType == typeof(string))
            {
                return new OmitSpecimen();
            }

            return new NoSpecimen(request);
        }
    }
}

My customization for AutoFixture implements the ISpecimenBuilder interface and thereby overrides the Create-method. The parameters send to the Create-method are a bit of a secret. For each property which needs a random value, AutoFixture invokes the Create method with a PropertyInfo instance. On the PropertyInfo we can use standard reflection to check if the current property needing a value is of type string and has a name equals “Id”. In this case I return a new OmitSpecimen, which tells AutoFixture not to set the Id property. Otherwise I return a new NoSpecimen, which tells AutoFixture that this specimen builder didn’t assign a value to the property.

Specimen builders need to be registered manually. When done, our code sample looks like this:

var fixture = new Fixture();
fixture.Customizations.Add(new NoIdSpecimenBuilder());
var model = fixture.CreateAnonymous<MyModel>();

Nice and clean! As indicated earlier, I don’t really like the object parameter send to the Create-method on specimen builders. I think that a generic interface making it possible to specify a subclass of ISpecimenBuilder<PropertyInfo> would be a lot cleaner and make it possible to strongly type the request parameter of the Create-method.