AutoFixture and Custom DataAnnotations

Currently, AutoFixture supports the following validation attributes:

  • RangeAttribute
  • RegularExpressionAttribute
  • StringLengthAttribute

To support a custom validation attribute you'll have to extrapolate from one of the following groups:

  • RangeAttributeRelay, RangedNumberRequest, RangedNumberGenerator
  • RegularExpressionAttributeRelay, RegularExpressionRequest, RegularExpressionGenerator
  • StringLengthAttributeRelay, ConstrainedStringRequest, ConstrainedStringGenerator

And then, you'll have to add the newly created Relay and Generator classes as Customizations.


I didn't want to bother creating a Relay, Request and Generator for each custom attribute I had in my solution. Instead, I create an ISpecimenBuilder that handled everything in one place.

In my case, I only have custom attributes on strings. Some custom attributes I created that I was looking for were AlphaNumericAttribute, and EnumAttribute.

So something like this:

public class CustomAnnotationsBuilder : ISpecimenBuilder
{
    private readonly Random rnd = new Random();

    public object Create(object request, ISpecimenContext context)
    {
        object result = new NoSpecimen(request);

        var pi = request as PropertyInfo;

        // do a few different inspections if it's a string
        if (pi != null && pi.PropertyType == typeof(string))
        {                
            // handle Enum attributes
            if (Attribute.IsDefined(pi, typeof(EnumAttribute)))
            {
                var enumAttribute = (EnumAttribute)Attribute.GetCustomAttribute(
                    pi, typeof(EnumAttribute));
                var allowedStrings = enumAttribute.GetAllowedStrings();
                return allowedStrings[rnd.Next(0, allowedStrings.Length)];
            }                
          
            if (Attribute.IsDefined(pi, typeof(StringLengthAttribute)))
            {
                var stringLengthAttribute = (StringLengthAttribute)Attribute.GetCustomAttribute(
                    pi, typeof(StringLengthAttribute));
                minLength = stringLengthAttribute.MinimumLength;
                maxLength = stringLengthAttribute.MaximumLength;

                // do custom string generation here
                return generatedString;
            }

            if (Attribute.IsDefined(pi, typeof(AlphaNumericAttribute)))
            {
                // do custom string generation here
                return generatedString;
            }

            return result;
        }

        return result;
    }

Then I could add that to AutoFixture like so:

Fixture.Customizations.Add(new CustomAnnotationsBuilder());
Fixture.Customize(new NoDataAnnotationsCustomization()); // ignore data annotations since I'm handling them myself

And that was it!

Tags:

Autofixture