There are many validation frameworks out there but sometimes you need to build one your own. Let’s see how easy it is to build a simple validation framework in TDD style using functionalities in System.ComponentModel.DataAnnotations. This is a rather new assembly introduced in .NET Framework 3.5, mainly used to define metadata for ASP.NET Dynamic Data controls. We’re going to use the validation attributes containing in this assembly to validate our entities and models. There’s a contrib project on CodePlex which contains extentions to this assembly, if you need to use it further.

If you need to use a more powerful validation framework, it is advised to use CastleValidator, NHibernate Validator or other validation frameworks out there.

Project Structure

For starters, let’s create a project structure. We need two projects, one for the unit tests with reference to NUnit assemblies, and another project containing our actual validation framework. Remember to add a reference to System.ComponentModel.DataAnnotations assembly.

When developing in TDD style, you write a failing test before you write the actual code, then implement the functionality and make the test pass. After the test is passed, you can do some refactoring and continue the procedure from the start.

Other development styles that also use “some” kind of test or unit testing should not be thought of as TDD. The emphasis on TDD style is to write the test first, not after the implementation which is usually referred to as Test After style.

TDD Workflow

As the workflow suggests, let’s create our very first test. We want to test that the default validator instance can validate a model / entity. Here’s the test:

1
2
3
4
5
6
7
8
9
10
11
12
[TestFixture]
public class ValidatorTests
{
[Test]
public void Can_Validate_Models()
{
var validator = new DefaultValidator() as IValidator;
var model = new Person();

Assert.DoesNotThrow(() => validator.Validate(model));
}
}

As you can see, the test won’t even compile. We need to create missing classes and compile the solution first to make sure the test fails.

If you’re using Resharper addin, it can create almost every missing piece of the puzzle for you. To do this, click the missing class, interface or method, press ALT + Enter and select proper option from appearing menu. Resharper has a plugin called TDD Productivity plugin which helps you create missing types in referenced projects more easily.

Create a new interface in the project containing the implementation named “IValidator” and create the “Validate” method:

1
2
3
4
public interface IValidator
{
void Validate<TEntity>(TEntity entity);
}

We need a default implementation of “IValidator”, to no surprise named “DefaultValidator”:

1
2
3
4
5
6
7
public class DefaultValidator : IValidator
{
public void Validate<TEntity>(TEntity entity)
{
throw new NotImplementedException();
}
}

The last remaining bit before we can compile the project is to create a Person class in our test project. This will be used as the object which we’ll try to validate:

1
2
3
public class Person
{
}

Let’s compile and run the test. The test will fail as expected. Since our test was over simplified, making it pass would be as easy as removing the exception:

1
2
3
public void Validate<TEntity>(TEntity entity)
{
}

Not much of an implementation. Let’s create a new test and change the Validate method to return a list of validation errors.

1
2
3
4
5
6
7
8
9
10
11
12
13
[Test]
public void Validate_Returns_ValidationErrors()
{
//Arrange
var validator = new DefaultValidator() as IValidator;
var model = new Person();

//Act
var result = validator.Validate(model);

//Assert
Assert.That(result.Count, Is.EqualTo(0));
}

The Arrange-Act-Assert (or AAA) is a readable test authoring pattern. Tests written in AAA are composed of 3 phases, Arrange in which test preconditions are defined, Act in which code under test is called and Assert in which results are inspected and failures are reported.

Once again, the code won’t compile. Let’s create missing pieces by changing the method signatures and adding ValidationError class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public interface IValidator
{
IList<ValidationError> Validate<TEntity>(TEntity entity);
}

public class DefaultValidator : IValidator
{
public IList<ValidationError> Validate<TEntity>(TEntity entity)
{
return null;
}
}

public class ValidationError
{
public ValidationError(string property, string errorMessage)
{
Property = property;
ErrorMessage = errorMessage;
}

public string Property
{
get;
private set;
}

public string ErrorMessage
{
get;
private set;
}
}

Running the test, we’ll see that it won’t pass. Again, having “Doing the simplest thing possible” rule in mind, the implementation would be easy as:

1
2
3
4
5
6
public IList<ValidationError> Validate<TEntity>(TEntity entity)
{
var errors = new List<ValidationError>();

return errors;
}

An important TDD principal is to “Do the simplest thing possible that works”. This is closely related to YAGNI.

We should have two passing tests so far.

Passing Test

It is time to implement the actual validation method. Let’s start by creating a failing test:

1
2
3
4
5
6
7
8
9
10
11
12
[Test]
public void Validating_Invalid_Model_Will_Return_Errors()
{
var validator = new DefaultValidator() as IValidator;
var model = new Person { Firstname = "John" };

var result = validator.Validate(model);

Assert.That(result.Count, Is.EqualTo(1));
Assert.That(result[0].Property, Is.EqualTo("Lastname"));
Assert.That(result[0].ErrorMessage, Is.EqualTo("Lastname is required."));
}

Before we run the test, let’s add missing properties to our Person class and decorate it with some validation attributes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Person
{
[Required(ErrorMessage = "Firstname is required.")]
public string Firstname
{
get; set;
}

[Required(ErrorMessage = "Lastname is required.")]
public string Lastname
{
get; set;
}
}

Let’s run the test which will fail at this state. We’re going to use reflection to read the attributes on the model and see if the property values matches the decorating attribute. Combined with a LINQ query the implementation would be:

1
2
3
4
5
6
7
8
9
10
11
12
public IList<ValidationError> Validate<TEntity>(TEntity entity)
{
var type = typeof (TEntity);
var properties = TypeDescriptor.GetProperties(type);
var errors = from PropertyDescriptor pd in properties
let value = pd.GetValue(entity)
from attrib in pd.Attributes.OfType<ValidationAttribute>()
where !attrib.IsValid(value)
select new ValidationError(pd.Name, attrib.ErrorMessage);

return errors.ToList();
}

If you run the test, you still see one failing test, but that is actually another test that used to pass. So what went wrong? Check out the Assertion we made in test “Validate_Returns_ValidationErrors”. The problem is that we have not specified “Firstname” and “Lastname” property on Person instance, and we’re asserting that there won’t be any errors. To make this test pass again, we should pass a valid person instance. So let’s refactor this test into this:

1
2
3
4
5
6
7
8
9
10
[Test]
public void Validate_Returns_ValidationErrors()
{
var validator = new DefaultValidator() as IValidator;
var model = new Person { Firstname = "John", Lastname = "Doe" };

var result = validator.Validate(model);

Assert.That(result.Count, Is.EqualTo(0));
}

We’re almost done. There are some things we can still improve, like reading the error message from resource files instead of ErrorMessage property but I’m satisfied with this implementation so I won’t do it in this post. Finally, let’s see how good we did implementing this using TDD style by creating a coverage report.

Coverage Report

Programming in TDD style has lots of “hidden” benefits too: The effort making things testable will lead you to decoupled components and by creating the tests first, you’ll have wider range of unit test compared to test-after style, hence better test coverage.

You can download the source code of this post here.