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!