Type Safe Unit Testing of Data Annotations Validations
November 13th 2009Were using the Data Annotations Validators in ASP.Net MVC 2.0, but there wasnt a really great way to unit test them. Ive read Brad Wilsons blog post about how to set them up and unit test them, but I didnt like how you relied on the name of the property as a string.
// Arrange
var propertyInfo = typeof(Contact).GetProperty("FirstName");
It would be nicer if that used lambda expressions to get the name of the property, so I set out to do just that. Before we begin, I need to remind you, as always, I dont know what Im doing. This is my first tiny venture into expression trees so it is possible isnt the best way to do things, but so far, its working for me.
I created a base Specification to wrap up a lot of the messy bits of getting property names by lambdas. I took a look at how the ASP.Net MVC helpers did it and roughly followed their pattern.
public class DataAnnotationSpecification<T> : Specification
{
public PropertyInfo Property(Expression<Func<T,object>> op)
{
return Property(GetInputName(op));
}
public PropertyInfo Property(string propertyName)
{
return typeof (T).GetProperty(propertyName);
}
public static string GetInputName<TProperty>(Expression<Func<T, TProperty>> expression)
{
// not sure I totally understand this
if (expression.Body.NodeType == ExpressionType.Convert)
{
var ue = (UnaryExpression)expression.Body;
return GetInputName((MemberExpression) ue.Operand );
}
if (expression.Body.NodeType == ExpressionType.MemberAccess)
{
return GetInputName((MemberExpression)expression.Body);
}
throw new NotImplementedException();
}
private static string GetInputName(MemberExpression memberExpression)
{
return memberExpression.Member.Name;
}
}
I know this probably doesnt cover an exhaustive list of expressions, but were only talking about Property access tests here so were probably in the clear.
To make things a bit nicer, I also created some extensions off PropertyInfo to help setup their assertions.
public static class AttributeAssertions
{
public static void ShouldNotBeRequired(this PropertyInfo @this)
{
@this.ShouldNotHaveAttribute<RequiredAttribute>();
}
public static void ShouldBeRequired(this PropertyInfo @this)
{
@this.ShouldHaveAttribute<RequiredAttribute>();
}
private static T PropertyAttributeOn<T>(ICustomAttributeProvider propertyInfo)
{
return propertyInfo.GetCustomAttributes(typeof (T), false)
.Cast<T>()
.FirstOrDefault();
}
public static void ShouldHaveAttribute<T>(this PropertyInfo @this)
{
PropertyAttributeOn<T>(@this).ShouldNotBeNull();
}
public static void ShouldNotHaveAttribute<T>(this PropertyInfo @this)
{
PropertyAttributeOn<T>(@this).ShouldBeNull();
}
}
We only have the ShouldBeRequired and ShouldNotBeRequired right now, but you could see it will be easy to extend upon them. The finished spec looks something like this.
public class When_testing_the_state_of_the_CustomerViewData
: DataAnnotationSpecification<CustomerViewData>
{
[Then] public void the_customer_first_name_is_required()
{
Property(x => x.CustomerFirstName).ShouldBeRequired();
}
[Then] public void the_customer_middle_name_is_not_required()
{
Property(x => x.CustomerMiddleName).ShouldNotBeRequired();
}
[Then] public void the_customer_last_name_is_required()
{
Property(x=>x.CustomerLastName).ShouldBeRequired();
}
[Then] public void the_email_address_is_required()
{
Property(x=>x.CustomerEmailAddress).ShouldBeRequired();
}
}
Thats not so bad, and it stands up to any refactoring we might do.