A brief recap of the Test Data Builder design pattern with examples in C#.

This is the first in a series of articles about the relationship between the Test Data Builder design pattern, and the identity functor.

In 2007 Nat Pryce described the Test Data Builder design pattern. The original article is easy to read, but in case you don't want to read it, here's a quick summary, with some of Nat Pryce's examples translated to C#.

The purpose of a Test Data Builder is to make it easy to create input data (or objects) for unit tests. Imagine, for example, that for a particular test case, you need an address in Paris; no other values matter. With a Test Data Builder, you can write an expression that gives you such a value:

var address = new AddressBuilder().WithCity("Paris").Build();

The address object explicity has a City value of "Paris". Any other values are default values defined by AddressBuilder. The values are there, but when they're unimportant to a particular test case, you don't have to specify them. To paraphrase Robert C. Martin, this eliminates the irrelevant, and amplifies the essentials of the test.

Address Builder #

An AddressBuilder could look like this:

public class AddressBuilder
{
    private string street;
    private string city;
    private PostCode postCode;
 
    public AddressBuilder()
    {
        this.street = "";
        this.city = "";
        this.postCode = new PostCodeBuilder().Build();
    }
 
    public AddressBuilder WithStreet(string newStreet)
    {
        this.street = newStreet;
        return this;
    }
 
    public AddressBuilder WithCity(string newCity)
    {
        this.city = newCity;
        return this;
    }
 
    public AddressBuilder WithPostCode(PostCode newPostCode)
    {
        this.postCode = newPostCode;
        return this;
    }
 
    public AddressBuilder WithNoPostcode()
    {
        this.postCode = new PostCode();
        return this;
    }
 
    public Address Build()
    {
        return new Address(this.street, this.city, this.postCode);
    }
}

The Address class is simpler than the Builder:

public class Address
{
    public string Street { getset; }
    public string City { getset; }
    public PostCode PostCode { getset; }
 
    public Address(string street, string city, PostCode postCode)
    {
        this.Street = street;
        this.City = city;
        this.PostCode = postCode;
    }
}

Clearly, this class could contain some behaviour, but in order to keep the example as simple as possible, it's only a simple Data Transfer Object.

Composition #

Given that AddressBuilder is more complicated than Address itself, the benefit of the pattern may seem obscure, but one of the benefits is that Test Data Builders easily compose:

var invoice = new InvoiceBuilder()
    .WithRecipient(new RecipientBuilder()
        .WithAddress(new AddressBuilder()
            .WithNoPostcode()
            .Build())
        .Build())
    .Build();

Perhaps that looks verbose, but in general, the alternative is worse. If you didn't have a Test Utility Method, you'd have to fill in all the required data for the object:

var invoice = new Invoice(
    new Recipient("Sherlock Holmes",
        new Address("221b Baker Street",
                    "London",
                    new PostCode())),
    new List<InvoiceLine> {
        new InvoiceLine("Deerstalker Hat",
            new PoundsShillingsPence(0, 3, 10)),
        new InvoiceLine("Tweed Cape",
            new PoundsShillingsPence(0, 4, 12))});

Here, the important detail drowns in data. The post code is empty because the PostCode constructor is called without arguments. This hardly jumps out when you see it. Such code neither eliminates the irrelevant, nor amplifies the essential.

Summary #

Test Data Builders are useful because they are good abstractions. They enable you to write unit tests that you can trust.

The disadvantage, as you shall see, is that in languages like C# and Java, much boilerplate code is required.

Next: Generalised Test Data Builder.


Comments

You got me to finally figure out how to post comments. :) Hope everything looks alright.

So first off, great article as always. You totally hit a subject which has been driving me nuts, personally and lately. I have been developing my first FluentAPI and have been running up against both aspects of immutability and query/command separation that you have done an excellent job of presenting here on your blog. It does seem that FluentAPI design and the builder pattern you present above deviate from these principles, so it would be great to hear a little more context and valuable insight from you on how you reconcile this. Is this perhaps a C# issue that is easily remedied in F#? Thank you in advance for any assistance and for providing such a valuable technical resource here. It's been my favorite for many years now.
2017-08-15 06:52 UTC

Mike, thank you for writing. The fluent interface that I show in this article is the most common form you see in C#. While it's not my preferred variation, I use it in this article because it's a direct translation of the style used in Nat Pryce's Java code.

Ordinarily, I prefer an immutable variant, but in C# this leads to even more boilerplate code, and I didn't want to sidetrack the topic by making this recap article more complicated than absolutely necessary.

You may be pleased to learn that future articles in this very article series will show alternatives in both C#, F#, and Haskell.

2017-08-15 07:30 UTC

Hi Mark, A while ago I made a generic builder for this exact purpose. I also made som helper extension methods, that could act as sort of an Object Mother. I quite like how it work and I have used it quite a few times. So, reading this post, I thought I'd put in a link to it, as it might be usefull to other readers.

Generic Builder with Object Mother Gist

It's all in one big gist and probably not very weel structured, but if you look at the class GenericBuilder it should be quite easily understood. The examples of extensionmethods can be seen towards the end of the file.

2017-08-15 07:42 UTC

I've used test data builders in C# just like this in the past, and couldn't decide whether I liked them or not, due to all the boilerplate.

I'm looking forward to the next few posts, thanks for doing this.

2017-08-18 11:43 UTC

Hi, Mark

In C# I starated to prefer to use a "parameterized object mother". Please take a look and tell me what out think about it: Address Object Mother Gist.

From my experience it is less and simplier code. It is also a bid easier to debug. Personally, the Object Mother is the first pattern when refactoring test data creationg and I use Fluent Test Data Builder only in more complex scenarios.

@JanD: Unfortunately, your solution would not work for immutable data structures (which I prefer).

2017-08-19 19:25 UTC

Robert, thank you for writing. I haven't seen that particular C# variation before, but it looks useful. I hope that as this article series progresses, it should become increasingly clear to the reader that the Test Data Builder pattern addresses various language deficiencies. (It has, by the way, for some time been a common criticism of design patterns in general that they are nothing but patches on language deficiencies. I don't think that I agree with that 100 percent, but I certainly understand the argument.)

Nat Pryce's original article about the Test Data Builder pattern is from 2007 with example code in Java. I don't know that much about Java, but back then, I don't think C# had optional arguments (as far as I can tell, that language feature was added in 2010). My point is that the pattern described a good way to model code given the language features that were available at the time.

As a general rule, I'm not a fan of C#'s optional argument feature (because I'm concerned what it does to forwards and backwards compatibility of my APIs), but used in the way you suggest it does look useful. Perhaps it does, indeed, address all the concerns that the Test Data Builder pattern addresses. I haven't tried it, so I can't really evaluate it (yet), but it looks like it'd be worth trying out.

My overall goal with this article series is, however, slightly different. In fact, I'm not trying to sell the Test Data Builder pattern to anyone. Rather, the point is that with better API design, and with better languages, it'd be largely redundant.

2017-08-21 06:33 UTC

Hi, Mark
Thank you for this post

I personally leverage Impromptu Interface. It could be also verbose but as you only provide meaningful data it fits to Robert C. Martin credo. And it avoids creating a lot of one-shot boilerplate code and/or noising existing classes with UT specific stuff.

2017-08-21 09:12 UTC

Romain, do you have an example of that, that you could share?

2017-08-21 09:49 UTC

Partial IAddress with City value only:

var address = new {
		    City = "Paris"
		  }.ActLike<IAddress>();
			

Partial IAddress with City value and partial IPostCode with ISO value only:

var address = new {
		    City = "Paris", 
		    PostCode = new {
				      ISO = "FR"
				   }.ActLike<IPostCode>()
		  }.ActLike<IAddress>();
			

Main drawback is verbosity but intent is pretty clear.
We could reduce nested code by splitting IAddress and IPostCode declarations but it also reduces intent: we do not care about IPostCode, we care about IAddress and IPostCode is only an implementation detail.

I heavily leverage region to cope with C# verbosity and to highlight common pattern - AAA in this case - so all this code is usually hidden in one ARRANGE region.
When I need multiple declaration I used sut (System Under Test) marker to highlight main actor.

2017-08-21 21:09 UTC

Do I understand it correctly that you'd have an interface like the following, then?

public interface IAddress
{
    string City { getset; }
}

I'm not sure that I quite follow...

2017-08-22 11:43 UTC

Mark, I tend to avoid setter in my interfaces so my domain objects usually are immutable and only expose getter.
My implementation are mainly internal which prevent them to be used directly from within UT assembly (without using InternalsVisibleTo attribute).
I have factories - which implementation are also internal - to build my objects.
I then use an IoC container to access factories and create my objects.

public interface IAddress
{
    string City { get; }
    string Street { get; }
    IPostCode PostCode { get; }
}

AddressBuilder lives in UT world so must be in another assembly to avoid noising my model.
To cope with my internal visibility constraint I have at least 2 options I can live with:

  1. Using InternalsVisibleTo attribute for my UT assembly to be able to seamlessly use my types
  2. Leveraging a test container to resolve my factory and then create my objects.
To deal with the immutable constraint I can create new ones within With methods. I can live with this too.

The main drawback remains the verbosity/burden of those methods.
Using Impromptu Interface to generate partial test data spares builder classes creation while keeping verbosity acceptable and intent clear.
Does it make sense?

2017-08-22 13:24 UTC

That helps clarify things, thank you.

I know that obviously, I could try for myself, but when you write

var address = new {
		    City = "Paris"
		  }.ActLike<IAddress>();

then what will be the value of address.PostCode?

2017-08-22 13:40 UTC
It throws an exception if accessed but live peacefully otherwise. It is why I talked about partial data.
You have to be aware of this. When your test focus on a single aspect of your class you can safely use it.
Imagine you are testing a City centric algorithm: you do not care about Street, Street number, Floor, and so on.
No need to create heavy/costly objects you can safely use a partial object which is only compliant with a part of the original interface.
The way you would have deal with if you had split IAddress interface into several parts namely IHaveACity, IHaveAStreet, ...
As it only declares what it needs to work the UT intent is pretty clear. As test builder it removes noisy stuff.
2017-08-22 14:22 UTC

Now I think I get it! Thank you for taking the time to explain.

2017-08-22 14:50 UTC

A slight variation on Robert Pajak's approach that allows writing an.Address() instead of unwieldy AddressObjectMother.Create(): Mother Factory.

Another usage sample: gist.

2017-09-12 9:16 UTC


Wish to comment?

You can add a comment to this post by sending me a pull request. Alternatively, you can discuss this post on Twitter or somewhere else with a permalink. Ping me with the link, and I may respond.

Published

Tuesday, 15 August 2017 06:20:00 UTC

Tags



"Our team wholeheartedly endorses Mark. His expert service provides tremendous value."
Hire me!
Published: Tuesday, 15 August 2017 06:20:00 UTC