To test our business code we always need some kind of test data. This tutorial explains how to do just that with the Object Mother pattern and why we should combine it with a Fluent Builder to create test data factories that are fun to work with.
Example Code
This article is accompanied by a working code example on GitHub.What do we Need a Test Data Factory For?
Let’s imagine that we want to create some tests around Invoice
objects that are structured as shown in the figure below.
An Invoice
has a target Address
and zero or more InvoiceItems
,
each containing the amount and price of a certain product that is billed with the invoice.
Now, we want to test our invoice handling business logic with a couple of test cases:
- a test verifying that invoices with an abroad invoice address are sent to an invoicing service specialized on foreign invoicing
- a test verifying that a missing house number in an invoice address leads to a validation error
- a test verifying that an invoice with a negative total price is forwarded to a refund service
For each of these test cases, we obviously need an Invoice
object in a certain state:
- an invoice with an address in another country,
- an invoice with an address with a missing house number,
- and an invoice with a negative total price.
How are we going to create these Invoice
instances?
Of course, we can go ahead an create the needed Invoice
instance locally in each test case. But, alas, creating an Invoice
requires creating some InvoiceItems
and an Address
, too … that seems like a lot of boiler plate code.
Apply the Object Mother Pattern to Reduce Duplication
The example classes used in this article are rather simple. In the real world, classes like Invoice
, InvoiceItem
or Address
can easily contain 20 or more fields each.
Do we really want to have code that initializes such complex object graphs in multiple places of our test code base?
Bad test code structure hinders the development of new features just as much as bad production code, as Robert C. Martin’s Clean Architecture has once more brought to my attention (link points to ebooks.com; read my book review).
So, let’s try to keep test code duplication to a minimum by applying the Object Mother pattern.
The Object Mother pattern is essentially a special case of the Factory pattern used for creating test objects. It provides one or more factory methods that each create an object in a specific, meaningful configuration.
In a test, we can call one of those factory methods and work with the object created for us. If the pre-defined object returned by the Object Mother doesn’t fully meet our test requirements, we can go ahead and change some fields of that object locally so that it meets the requirements of our test.
In our example, the Object Mother might provide these factory methods for pre-defined Invoice
objects:
InvoiceMother.complete()
: creates a complete and validInvoice
object including sensibly configuredInvoiceItems
and a validAddress
InvoiceMother.refund()
: creates a complete and validInvoice
object with a negative total price
For our three test cases, we can then use these factory methods:
- To create an
Invoice
with an abroad address, we callInvoiceMother.complete()
and change thecountry
field of the address locally - To create an
Invoice
with a missing house number, we callInvoiceMother.complete()
and remove the house number from the address locally - To create an
Invoice
with a negative total price, we simply callInvoiceMother.refund()
The goal of the Object Mother pattern is not to provide a factory method for every single test requirement we might have but instead to provide ways to create a few functionally meaningful versions of an object that can be easily adapted within a concrete test.
Even with that goal in mind, over time, an Object Mother might degrade to the code equivalent of a termite queen, birthing new objects for each and every use case we might have. In every test case, we’d have a dependency to our Object Mother to create objects just right for the requirements at hand.
Each time we change one of our test cases, we would also have to change the factory method in our Object Mother. This violates the Single Responsibility Principle since the Object Mother must be changed for a lot of different reasons.
We stated above that we want to keep our test code base clean, so how can we reduce the risk for violating the Single Responsibility Principle?
Introduce the Fluent Builder Pattern to Promote the Single Responsibility Principle
That’s where the Builder pattern comes into play.
A Builder is an object with methods that allow us to define the parameters for creating a certain object. It also provides a factory method that creates an object from these parameters.
Instead of returning readily initialized objects, the factory methods of our Object Mother now return Builder objects that can be further modified by the client to meet the requirements of the specific use case.
The code for creating an Invoice with a modified address might look like this:
Invoice.InvoiceBuilder invoiceBuilder = InvoiceMother.complete();
Address.AddressBuilder addressBuilder = AddressMother.abroad();
invoiceBuilder.address(addressBuilder.build());
Invoice invoice = invoiceBuilder.build();
So far, we haven’t really won anything over the pure Object Mother approach described in the previous section.
Our InvoiceMother
now simply returns instances of InvoiceBuilder
instead of directly returning Invoice
objects.
Let’s introduce a fluent interface to our Builder. A fluent interface is a programming style that allows to chain multiple method calls in a single statement and is perfectly suited for the Builder pattern.
The code from above can now be changed to make use of this fluent interface:
Invoice invoice = InvoiceMother.complete()
.address(AddressMother.abroad()
.build())
.build();
But why should this reduce the chance for violating the Single Responsibility Principle in an Object Mother class?
With a fluent API and an IDE that supports code completion, we can let the API guide us in creating the object we need.
Having this power at our fingertips we’ll more likely configure the specific Invoice
we need in our test code and we’ll less likely create a new factory method in our Object Mother that is probably
only relevant four our current test.
Thus, combining the Object Mother pattern with a fluent Builder reduces the potential of violating the Single Responsibility Principle by making it easier to do the right thing.
May a Factory Method Call Another Factory Method?
When creating an Object Mother (or actually any other kind of factory), a question that often arises is: “May I call another factory method from the factory method I’m currently coding?”.
My answer to this question is a typical “yes, but…”.
Of course, we may take advantage of other existing Object Mothers. For instance, in the code of InvoiceMother
,
we may happily call AddressMother
and InvoiceItemMother
:
class InvoiceMother {
static Invoice.InvoiceBuilder complete() {
return Invoice.Builder()
.id(42L)
.address(AddressMother.complete()
.build())
.items(Collections.singletonList(
InvoiceItemMother.complete()
.build()));
}
}
But the same rules apply as in our client test code. We don’t want to add responsibilities to our factory method that don’t belong there.
So, before creating a custom factory method in an Object Mother we want to call from the factory method we’re currently coding, let’s think about whether we should rather use one of the pre-defined factory methods and customize the returned builder via fluent API to suit our requirements.
Conclusion
The Object Mother pattern by itself is a big help in quickly getting pre-defined objects to use in tests.
By returning Builders with a fluent API instead of directly returning object instances, we add a lot of flexibility to our test data generation, which makes creating new test objects for any given requirement a breeze. It supports the Single Responsibility Principle by making it easy to adjust created objects locally.
Further Reading
- Clean Architecture by Robert C. Martin, chapter 28 about the quality of test code (link points to ebooks.com)
- Martin Fowler on Object Mother
- Object Mother at java-design-patterns.com
- TestDataBuilder at wiki.c2.com