Fluent test data builder for unit tests

A while ago I came across the presentation Sustainable Test-Driven Development by Steve Freeman. In this great presentation Steve talks about writing unit-tests and how to avoid the danger of them growing to huge, complex, hard to read and hard to maintain pieces of code. One of the solutions he talked about was using ‘test data builders’ to compose an object graph of test data in a fluent way, which greatly improves the readability of the unit-test. For example:

Order o = anOrder()
          .WithOrderDate(DateTime.Now)
          .WithCustomer(aCustomer()
                        .WithName("John Doe")
                        .WithCity("Somewhere")
                        .Build())
          .WithOrderLine(anOrderLine()
                         .WithProduct(aProduct()
                                     .WithDescription("Windows Phone 7")
                                     .WithPrice(499)
                                     .Build())
                          .WithQuantity(2)
                          .Build())
           .Build();

My first thought was: great, but do we really need this in C# (the presentation was Java) since we have the new type initialization introduced in C# 3.5? Creating the same object graph using C# is very similar, e.g.:

Order o = new Order()
          {
            OrderDate = DateTime.Now,
            Customer = new Customer()
            {
              Name = "John Doe",
              City = "Somewhere"
            },
            OrderLines = new List<OrderLine>()
                         {
                           new OrderLine()
                           {
                             Product = new Product()
                             {
                               Description = "Windows Phone 7",
                               Price = 499
                             },
                             Quantity = 2
                           }
                         }
           };

However, this means that you have to make the set-method of the OrderLine collection public. If you don’t want that (or can’t because you use something like EF, in which collections are created internally) you end up with creating a new order and adding each new order line afterwards.

Which syntax is better? That is really a matter of personal opinion, but the builder solution comes with additional benefits like:

  • default values of properties (e.g. required values) can be set in the builder. This means that a unit-test only has to set those properties of an object that are relevant for the specific test, not bothering with other properties. This makes the test smaller and more readable;
  • builders can be extended with all sorts of With… methods. For example a method like WithInvalidOrderDate(), which encapsulates an order date of 1990/1/1. This is more readable than .WithOrderDate(new DateTime(1990, 1, 1)) because the method explains to the reader of the test that the order has an invalid order date.

T4

The downside of these builders is of course that they have to be coded, which is not the most fun part and decreases your productivity. Therefore I’ve started a T4 (Text Template Transformation Toolkit) template that generates a (partial) builder for each domain class. To extent a builder you can create a new partial class with the same name. Do not change the generated files because they will be overwritten by the generator.

Note that this is only the first version of the template. The next version will include better implementation for compiling the source files, support for other collections than ICollection only and a real implementation of singularizing the collection name.

Conclusion

I’ve used these builders in a several projects now and I must say that it really adds value to the readability and quality of the unit tests. The additional time required to create the builders is a small price for what you get in return, especially when you use a code generator.

Download

The sample project, including the T4 template, can be downloaded from this link: FluentTestDataBuilderExampleWithT4.zip.

UPDATE 27/7/2010 new version of the T4 template that includes: bugfixing, refactoring and use of EF’s PluralizationService:GenerateTestDataBuilders.tt v0.2.