
If you are strict about your use of constructors and immutable value objects, constructing objects in a valid state can be a bit of a chore.
Usually in application code, such objects are constructed in few places and all the information required by the constructor is at hand, having been provided by user input, obtained from a database query or received in a message for example. In tests, on the other hand, you have to provide all those constructor arguments every time you want to create an object, whether to test its behaviour or to create a value to use as input to the code being tested.
Invoice invoice = new Invoice(
new Recipient("Sherlock Holmes",
new Address("222b Baker Street",
"London",
new PostCode("NW1", "3RX"))),
new InvoiceLines(
new InvoiceLine("Deerstalker Hat",
new PoundsShillingsPence(0, 3, 10)),
new InvoiceLine("Tweed Cape",
new PoundsShillingsPence(0, 4, 12))));
The code to create all those objects makes tests messy and hard to read and fills the tests with lots of unnecessary information that has nothing to do with the behaviour being tested. It also makes tests brittle: changes to the constructor arguments or the structure of the objects will break many tests.
The Object Mother pattern is one attempt to avoid this problem. An Object Mother is a class that contains a number of (usually static) Factory Methods that create objects for use in tests. For example, we could create an Object Mother for invoices we want to use in tests:
Invoice invoice = TestInvoices.newDeerstalkerAndCapeInvoice();
An Object Mother helps keep tests readable by moving the code that creates new objects out of the tests themselves and giving clear names to the objects being constructed. It also helps maintain the test data by gathering the code that creates new objects together into the Object Mother class and allowing it to be reused between tests.
However, the Object Mother pattern does not cope at all well with variation in the test data. Every time programmers need some slightly different test data they add another factory method to the Object Mother.
Invoice invoice1 = TestInvoices.newDeerstalkerAndCapeAndSwordstickInvoice(); Invoice invoice2 = TestInvoices.newDeerstalkerAndBootsInvoice(); ...
Over time, the Object Mother becomes bloated, messy and hard to maintain. Either programmers add new factory methods without refactoring, in which case the Object Mother becomes full of duplicated code, or programmers refactor diligently, in which case the Object Mother becomes full of many, many fine-grained methods that each contain little more than a single new statement.
A solution is to use the Builder Pattern. For each class you want to use in a test, create a Builder for that class that:
For example, a builder of Invoice objects might look like:
public class InvoiceBuilder {
Recipient recipient = new RecipientBuilder().build();
InvoiceLines lines = new InvoiceLines(new InvoiceLineBuilder().build());
PoundsShillingsPence discount = PoundsShillingsPence.ZERO;
public InvoiceBuilder withRecipient(Recipient recipient) {
this.recipient = recipient;
return this;
}
public InvoiceBuilder withInvoiceLines(InvoiceLines lines) {
this.lines = lines;
return this;
}
public InvoiceBuilder withDiscount(PoundsShillingsPence discount) {
this.discount = discount;
return this;
}
public Invoice build() {
return new Invoice(recipient, lines, discount);
}
}
Tests that don't care about the precise values in an Invoice can create one in a single line:
Invoice anInvoice = new InvoiceBuilder().build();
Tests that want to use specific values can define them inline without filling the test with unimportant details:
Invoice invoiceWithNoPostcode = new InvoiceBuilder()
.withRecipient(new RecipientBuilder()
.withAddress(new AddressBuilder()
.withNoPostcode()
.build())
.build())
.build();
I've used Builders for creating test data on a couple of projects now and I've found that, compared to Object Mothers, they make it much easier to create test data in-line in the test code without making tests brittle or creating lots of duplication. Tests are isolated from those aspects of the objects' structure that have no bearing on the test. For example, code that creates the invoice with no postcode needs to know that an invoice has a recipient, that has an address, that has a postcode, but has no further dependencies on the structure of invoices, recipients and addresses. You can add constructor arguments without breaking tests at all. Removing constructor arguments is easy as well with modern refactoring IDEs.
Another benefit is that the test code is easier to write and read because the parameters are clearly identified. Compare:
TestAddresses.newAddress(
"Sherlock Holmes",
"222b Baker Street",
"London",
"NW1");
to:
new AddressBuilder()
.withName("Sherlock Holmes")
.withStreet("222b Baker Street")
.withCity("London")
.withPostCode("NW1", "3RX")
.build();
Nothing in the first example will tell you that "London" has been accidentally passed as the second street line instead of the city name.
In some cases, Builders have so improved the code that they ended up being used in the production code as well.
Further techniques for using Test Data Builders:
Update: thanks to Richard Hansen for pointing out a typo in the builder code, which is now fixed.
Unlike C#, Java has no language support for event notification. Instead, Java classes usually follow a set of programming conventions, defined by the Java Beans framework, for defining notifications in terms of listener interfaces and methods for connecting and disconnecting event listeners and event sources.
In the Java Bean conventions, notifications are typed and grouped by "listener" interfaces. Each listener interface extends EventListener and defines a methods for each notification that is delivered through that interface. For example, the ContainerListener interface defines two notifications: componentAdded, announced when a component is added to the container, and componentRemoved, announced when a component is removed. Listener methods do not return results.
An object is declared as source of events of some type by implementing two methods, one to add a new listener to the event source and one to remove a listener from the event source. For example, a source of Sheep events would define the two methods:
An object that wants to receive notifications must implement the listener interface that defines the notifications and then be added as a listener to the source of events. The source of events must maintain a collection of registered listeners. To announce an event it must call the appropriate notification method of all of the listeners that have been registered with it.
As you can see, writing a class that announces Java Bean events is not an insignificant effort. What is a one-line declaration in C# requires a lot of boilerplate code in Java. The Java Bean event conventions and Java's static type system do not make it easy to write notification code in a way that can be reused in many classes for different listener types. As a result, notification is not used as often as it could be in Java code. It is usually easier to pass a callback interface to an object's constructor than it is to define Java Bean events on the class.
The result is that classes are more tightly coupled than necessary. You must pass a listener to the constructor of the source of events even when there is no need to consume the notifications.
Worse, it becomes impossible to create the object and the listeners at different times and then connect them at a later time. This is a form of what I call "Temporal Coupling". The objects appear to be decoupled because they communicate through interfaces but there is an implicit, hidden coupling between the time at which the two objects can do things. In this case, the listener must exist before you create the source of events.
Luckily I have a trick up my sleeve. I have written a cunning class, Announcer, that uses the reflection API and Java 5 generics to implement the Java Bean event model for any listener interface. Although it still requires more than one-line, it makes it much easier to add Bean events to any class. Here's how it's used to make a class a source of Sheep events:
public class Sheep {
private Announcer<SheepListener> sheepListeners = Announcer.to(SheepListener.class);
...
public void addSheepListener(SheepListener listener) {
sheepListeners.addListener(listener);
}
public void removeSheepListener(SheepListener listener) {
sheepListeners.removeListener(listener);
}
protected void announceSheepDipped() {
sheepListeners.announce().sheepDipped(this);
}
protected void announceSheepSheared() {
sheepListeners.announce().sheepSheared(this);
}
...
}
Currently the Announcer class is sitting as an example in the jMock project. It's only one class (and a unit test), so I don't think it's worth giving it its own project and JAR file. I usually just copy it into whatever project I'm writing and move it into one of the project's packages. Feel free to do the same.
In Java 5, an application's main function can be declared with varargs parameters instead of an array of Strings. For example:
public class Main {
public static void main(String... args) {
for (String arg : args) System.out.println(arg);
}
}
Not a particularly astounding revelation: under the hood, varargs parameters are passed to the function as an array so the JVM sees no difference between a varargs main function and one declared to take a String array. However, it's easier to call a varargs main function from other Java code – in end-to-end tests for instance – and the resulting code is easier to read. For example:
private void runProgram() {
Main.main("hello", "world");
}
instead of
private void runProgram() {
Main.main(new String[]{"hello", "world"});
}
Because of this I've started declaring all my main functions with varargs parameters.