Create test objects using Builder pattern and methods chaining

Mariusz Wojcik

In my previous post “Create test objects using Factory Methods or Object Mother pattern” I described how Factory Method and Object Mother patterns can be used to help with creating object instances. Presented solution allows to remove code duplication from unit tests making them easier to adopt changes in tested objects, but doesn’t help much with test code readability, which is very important factor in maintaining unit tests. Using sample Address class from the previous article, imagine following code:

Address address = new Address("Poland", "Slask", "Zabrze", "Chrobry", "13", "46-230");

For someone who doesn’t know a lot about Poland’s geography and addressing system, it is really hard to recognise which parameter represents County, City or Street, and when it comes to numbers the situation is even worse. To solve the mystery meaning of parameters, reader must go to class definition or use help from tools such as IntelliSense. Maybe it doesn’t seem like much effort, but it breaks the reading process and de-concentrates reader. It is like reading a book in foreign language with many new words. The reader spends more time going through the dictionary than reading a book and in effect doesn’t remember much from what she read. Soon, the reader stops checking every unknown word in the dictionary and starts to assume its meaning from the context, although in many cases the assumption is wrong (Some of the novels I read when I was learning English I read several times, and every time it was like reading different book). To help the reader with understanding the unit test, it must be written in a way that there is no need to leave the page or assume any meaning, but code is kept conscience and free from duplication.

Below is the AddressBuilder class:

internal sealed class AddressBuilder
{
    #region private members
 
    private string _country;
    private string _county;
    private string _city;
    private string _street;
    private string _houseName;
    private string _postCode;
 
    #endregion
 
    public Address Build()
    {
        return new Address(this._country, this._county, this._city, this._street, this._houseName, this._postCode);
    }
 
    public AddressBuilder WithCountry(string country)
    {
        this._country = country;
        return this;
    }
 
    public AddressBuilder WithCounty(string county)
    {
        this._county = county;
        return this;
    }
 
    public AddressBuilder WithCity(string city)
    {
        this._city = city;
        return this;
    }
 
    public AddressBuilder WithStreet(string street)
    {
        this._street = street;
        return this;
    }
 
    public AddressBuilder WithHouseName(string houseName)
    {
        this._houseName = houseName;
        return this;
    }
 
    public AddressBuilder WithPostCode(string postCode)
    {
        this._postCode = postCode;
        return this;
    }
 
    public AddressBuilder InGuildfordUK()
    {
        this._country = "United Kingdom";
        this._county = "Surrey";
        this._city = "Guildford";
        this._street = "St. George Av.";
        this._houseName = "Elliot House";
        this._postCode = "GU3 1DA";
        return this;
    }
 
    public AddressBuilder InBrentfordUK()
    {
        this._country = "United Kingdom";
        this._county = "Middlesex";
        this._city = "Brentford";
        this._street = "Windmill Road";
        this._houseName = "5";
        this._postCode = "TW8 9NA";
        return this;
    }
}

The two methods at the end: InGuildfordUK and InBrentfordUK make building sample addresses easier and can be used in tests, where the exact content is not as important as the fact that the addresses are different. The example can be a test of collection with addresses which doesn’t allow duplicated entries:

// addresses is a collection which doesn't allow duplicated entries
 
addresses.Add(new AddressBuilder().InBrentfordUK().Build());
addresses.Add(new AddressBuilder().InGuildfordUK().Build());

By using descriptive names we are telling a reader that the former address is in Guildford in the United Kingdom, while the latter address is in Brentford, UK.

References: