Invoice invoiceWithNoPostcode = new InvoiceBuilder()
.withRecipient(new RecipientBuilder()
.withAddress(new AddressBuilder()
.withNoPostcode()
.build())
.build())
.build();
I think I can improve on this and achieve code that looks like this:
MutableInvoice invoiceWithNoPostcode = new MutableInvoice()
.withRecipient(new MutableRecipient()
.withAddress(new MutableAddress()
.withNoPostcode()));
Instead of creating a builder for each domain class I've created a mutable extension to each domain class, it is the mutable extension I'm using in the test.
I don't quite like the code above, I think the following looks better:
Invoice invoiceWithNoPostcode = new Invoice()
.withRecipient(new Recipient()
.withAddress(new Address()
.withNoPostcode()));
I've changed the code to use the same name for the mutable extension (with a different package of course) because I figure the tests can just refer to the mutable versions and it saves me having the 'Mutable' prefix on everything. Naturally I only make the mutable extensions available for testing.
Here's how the mutable Invoice extension might look:
package model.mutable;
public Invoice extends model.Invoice {
public Invoice(Recipient recipient, InvoiceLines invoiceLines) {
super(recipient, invoiceLines);
}
public Invoice() {
// choose the same defaults here as you would in the InvoiceBuilder
this(null, null);
}
public Invoice withRecipient(Recipient recipient) {
return new Invoice(recipient, getInvoiceLines());
}
}
I've had to add protected accessors to the domain model; this bothers me a little as they make it easier to slip towards an anemic data model. It might be worth using some reflective code in the extension class instead:
public class Invoice extends model.Invoice {
...
private InvoiceLines getInvoiceLines() {
return new ReflectiveAccessor().get(this, "invoiceLines", InvoiceLines.class);
}
}
public class ReflectiveAccessor {
publicT get(Object fieldOwner, String fieldName, Class fieldClass) {
...
}
}
3 comments:
I don't really think that's an improvement Stacy when you have to change visibility modifiers or mess around with reflection. The builder pattern is cleaner in my eyes.
I can see that the implementation of the mutable extension has more complexity than the builder, but I wasn't arguing that that was simpler but that the resulting tests would look simpler.
I prefer having code which is simple to use and simple in its implementation, but given a choice between simple to use or simple to implement I'd choose the former.
I don't find that to be an improvement.
We came up with the Test Data Builder pattern to allow us to use immutable value objects, information hiding and encapsulation.
I'd rather keep my domain model clean and add a bit of extra code in the test suite than have a sloppy domain model that appears to provide hooks for change that will degrade the system design when used. E.g. I prefer having code that is simple to *evolve* than one that is simple to to implement.
Post a Comment