Skip to content

Fluent Test Data Builders

by Alex Peck on May 22nd, 2012

Sometimes the setup code required for creating unit test dependencies can grow to the point where it distracts from the intent of the test. Instead of repeating long winded constructor calls over and over, or creating hard to reuse helper methods on individual test classes, we can use a builder class to create the test dependencies. If we give our builder a fluent interface, the tests can be both compact and readable.

Let’s say we have a Person class like this:

public class Person
{
   private readonly string firstName;
   private readonly string lastName;
   private readonly DateTime dateOfBirth;
   private readonly int shoeSize;
 
   public Person(
      string firstName, 
      string lastName, 
      DateTime dateOfBirth, 
      int shoeSize)
   {
      this.firstName = firstName;
      this.lastName = lastName;
      this.dateOfBirth = dateOfBirth;
      this.shoeSize = shoeSize;
   }
 
   public string FirstName { get { return this.firstName; } }
   public string LastName { get { return this.lastName; } }
   public int ShoeSize { get { return this.shoeSize; } }
 
   public int Age()
   {
      DateTime today = DateTime.Today;
      int age = today.Year - this.dateOfBirth.Year;
      return (this.dateOfBirth > today.AddYears(-age)) 
         ? --age 
         : age;
   }
}

And we want to test a YoungerThan method like the one below. It doesn’t depend on any part of Person except Age.

public static IEnumerable YoungerThan(
    this IEnumerable people, 
    Person person)
{
    return people.Where(p => p.Age() < person.Age());
}

It might be a bit tedious to have to create a collection of People to test this method, because the Person constructor requires us to provide a number of arguments which are not relevant to the method under test. Enter the PersonBuilder class, which provides suitable defaults and a fluent interface for mutating the Person instance it creates.

public class PersonBuilder
{
    protected string FirstName { get; set; }
    protected string LastName { get; set; }
    protected DateTime DateOfBirth { get; set; }
    protected int ShoeSize { get; set; }
 
    public PersonBuilder()
    {
        this.FirstName = "Robin";
        this.FirstName = "Banks";
        this.DateOfBirth = new DateTime(1946, 3, 26);
        this.ShoeSize = 10;
    }
 
    public PersonBuilder WithFirstName(string firstName)
    {
        this.FirstName = firstName;
        return this;
    }
 
    public PersonBuilder WithLastName(string lastName)
    {
        this.LastName = lastName;
        return this;
    }
 
    public PersonBuilder WithAge(int age)
    {
        this.DateOfBirth = DateTime.Today.AddYears(-age);
        return this;
    }
 
    public PersonBuilder WithShoeSize(int shoeSize)
    {
        this.ShoeSize = shoeSize;
        return this;
    }
 
    public Person Build()
    {
        return new Person(
            this.FirstName, 
            this.LastName, 
            this.DateOfBirth, 
            this.ShoeSize);
    }
}

With the PersonBuilder, our test might look a little like the test method below. Notice that we are able to express all the test data in terms of the test, and we didn’t need to introduce a lot of distracting values which are not part of the test.

[TestMethod]
public void YoungerThan_WithOneYoungerPerson_ReturnsOne()
{
    Person referencePerson = new PersonBuilder()
                                .WithAge(21).Build();
 
    var people = new[] 
    { 
        referencePerson, 
        new PersonBuilder().WithAge(20).Build(), 
        new PersonBuilder().WithAge(22).Build()
    };
 
    var youngerPeople = people.YoungerThan(referencePerson);
 
    Assert.AreEqual(1, youngerPeople.Count());
}
No comments yet

Leave a Reply

Note: XHTML is allowed. Your email address will never be published.

Subscribe to this comment feed via RSS