Sunday, September 30, 2007

Test Data Builder versus Object Mother

When writing unit tests, I frequently have an issue with creating objects that contain some test data. One approach is to call the constructor of the class at hand in every test case, like so:

[Test] public void TestCase1() { Customer customer = new Customer("Homer", "Simpson"); // further implementation } [Test] public void TestCase2() { Customer customer = new Customer(null, "Simpson"); // further implementation } [Test] public void TestCase3() { Customer customer = new Customer("Homer", null); // further implementation }

The problem with this approach becomes clear when adding a new argument to the constructor of the Customer class. Now I have to make a change to all tests that create a Customer object.

What I did to overcome this issue, was to use the Object Mother pattern. I created a static class called TestFactory with a bunch of constant fields and methods that create all kinds of objects: 

internal static class TestFactory { public const String FirstName = "Homer"; public const String LastName = "Simpson"; private static Customer CreateCustomer(String firstName, String lastName) { return new Customer(firstName, lastName); } public static Customer CreateValidCustomer() { return CreateCustomer(FirstName, LastName); } public static Customer CreateCustomerWithoutFirstName() { return CreateCustomer(null, LastName); } public static Customer CreateCustomerWithoutLastName() { return CreateCustomer(FirstName, null); } }

I didn't really like this approach. For every special case, I needed to add another factory method. I couldn't come up with something better either at the time, so ... until I read this fine article yesterday. Instead of using the Object Mother pattern, the author uses the Builder pattern.

public class CustomerBuilder { public String _firstName = "Homer"; public String _lastName = "Simpson"; public CustomerBuilder WithFirstName(String firstName) { _firstName = firstName; return this; } public CustomerBuilder WithLastName(String lastName) { _lastName = lastName; return this; } public Customer Build() { return new Customer(_firstName, _lastName); } }

The tests itself would look like this:

[Test] public void TestCase1() { Customer customer = new CustomerBuilder().Build(); // further implementation } [Test] public void TestCase2() { Customer customer = new CustomerBuilder() .WithFirstName(null) .Build(); // further implementation } [Test] public void TestCase3() { Customer customer = new CustomerBuilder() .WithLastName(null) .Build(); // further implementation }

Looks pretty neat huh? If you're interested in writing maintainable unit tests, then you should definitely give the article a good read. The examples are written in Java, so I don't know if that has got anything to do with it :-).

6 comments:

florisla said...

Not unlike the syntax helper of NUnit, no? /me likes that a lot.

A nice, simple pattern that makes for clean code. What could you ask more?

Jan Van Ryswyck said...

Applying the builder pattern this way makes the code very easy to read. It has this fluent interface thing going that makes it very easy to comprehend. I'm glad that you like it too.

Gary said...

Very nice ... your site is now bookmarked in my borwser.

Keep up the excellent blog!

Anonymous said...

should you write tests for CustomerBuilder?

Jan Van Ryswyck said...

No, you should not be writing unit tests for the builder classes because it is part of the unit test code. The code under test (e.g. the Customer class) makes sure that your unit test code is correct. In other words, the unit test code tests the production code and vice versa.

Colin Jack said...

We currently use an object mother style approach.

However in this example your only showing the builder specifying arguments to the constructor, do you also have parametrized creation methods or named state reaching methods on your builders (I'm just using the definitions from http://xunitpatterns.com/Creation%20Method.html).