Struggling for Competence

Test Data Setup with Fluent Builder Pattern

When writing tests I like to follow the arrange, act, assert pattern:

Arrange-Act-Assert: a pattern for arranging and formatting code in UnitTest methods. Each method should group these functional sections, separated by blank lines:

The first part of this pattern is explicit set-up of the test data in the test itself. I like this because you can see the connection between the test inputs and outputs, which makes the test easier to understand. I think once you've overcome the challenge of actually writing tests, the next big hurdle is making them comprehensible. I'm still working at this.

In the past I've often set-up test data like this:


        [Test]
        public void CrummySetup()
        {
            var request = new Request("GoodbyeCruleWorld")
                               {
                                   Fields = new List<Field>()
                                                {
                                                    new Field("XYZ", "Raleigh Chopper"),
                                                    new Field("ABC", "Ford Fiesta")
                                                }
                               };
        }

It's OK, but there are some problems. I want 3 things in my test data set-up:

You want to hide the new operator for refactorability and to hide constructor irrelevance. The easiest way to do this is with a static factory method, which creates a fully valid default object. You want to the syntax to be terse, and emphasise the data which matters. A nice way to do this is with a fluent interface. A fluent interface allows method chaining, in the simplest case by returning the original object.


       [Test]
        public void QuailitySetup()
        {
            var request = RequestBuilder.Create("NewWorldOrder")
                                        .WithField("XYZ", "Carbon Racing Bike")
                                        .WithField("ABC", "Bugatti Veyron");
        }

The syntax is terse, refactorable and hides irrelevance. Win, win, win. You implement it like this:


public static class RequestBuilder
{
    public static Request Create(string name)
    {
        return new Request(name);
    }
    public static Request WithField(this Request request, string code, string name)
    {
        request.Fields.Add(new Field(code, name));
        return request;
    }
}

Fluent Builder

If you check out the builder pattern in the Gang of Four book, you may be hard pressed to see the connection with the code shown here. However if you check out the consequences of the builder pattern you see:

Builder Pattern

Which is exactly what the fluent builder does. It moves the construction code into it's own class, and allows you to vary what's made by calling different fluent extension methods.

My colleague Rich came up with this idea last week while we were working together. If you look around you can see we were not the first folks to have or name this idea. I guess that's what makes it a pattern. A full code listing follows:


using System.Collections.Generic;
using NUnit.Framework;

namespace FluentDataBuilderDemo
{
    public class Field
    {
        public string Code { get; set; }
        public string Name { get; set; }

        public Field(string code, string name)
        {
            Code = code;
            Name = name;
        }
    }

    public class Request
    {
        public string Name { get; set; }
        public List<Field> Fields { get; set; }

        public Request(string name)
        {
            Name = name;
            Fields = new List<Field>();
        }
    }

    [TestFixture]
    public class ShowDataSetup
    {
        [Test]
        public void CrummySetup()
        {
            var request = new Request("GoodbyeCruleWorld")
                               {
                                   Fields = new List<Field>()
                                                {
                                                    new Field("XYZ", "Raleigh Chopper"),
                                                    new Field("ABC", "Ford Fiesta")
                                                }
                               };

            Assert.AreEqual(2, request.Fields.Count);
        }

        [Test]
        public void QuailitySetup()
        {
            var request = RequestBuilder.Create("NewWorldOrder")
                                        .WithField("XYZ", "Carbon Racing Bike")
                                        .WithField("ABC", "Bugatti Veyron");

            Assert.AreEqual(2, request.Fields.Count);
        }
    }

    public static class RequestBuilder
    {
        public static Request Create(string name)
        {
            return new Request(name);
        }
        public static Request WithField(this Request request, string code, string name)
        {
            request.Fields.Add(new Field(code, name));
            return request;
        }
    }
}