Shaun Abram
Java and Technology weblog
Code Camp: Building Better Tests in Java
Building Better Tests in Java
Speaker: Ted Young (same speaker from Session 2 on Agile)
A talk on using the Builder pattern to make tests easier
Started with an overview of the Builder Pattern:
Separate the construction of a (complex) object from its representation
and used a pizza analogy to explain it:
A Pizza director says a pizza is built using a base, sauce, toppings
Possibe Builders could then be
Using a factory method, can result in too many parameters.
So, you can create a factory method that defaults many values to solve this – better but…
You may want to control certain values at certain times and things then stars getting complicated (too many methods/combinations).
This is basically the Object Mother pattern.
Can cause Merge hell, Copy-n-paste, too long (all sounds familiar!)
Solution…
Builders and chained Methods…
Lots of defaults (if you don’t specify, use default value, e.g. randome nums, def values…)
e.g.
Account account = new AccountBuilder()
.withDefaultBillingPlan()
.withPolocies(new PolicyBuilder.withDefaultPolicyPeriod())
.create();
where for example, withDefaultBillingPlan() returns a Builder rather than an object such as BillingPlan, Account etc
Advantages to this approach:
- Specify data not possible thru the UI (although isn’t this just an advantage of test case sin general, rather than a Builder pattern specifically?)
- Data is always consistent (but not necessarily valid – you want to test ‘invalid’ scenarios/objects also!)
- Fills in unspecified data with reasonable defaults (reasonable = wont throw exception)
- Extensible
- Can export to SQL
Can migrate bit my bit from huge DataGen/DomainFactory class to Builders,
Or can do big bang.
Writing Builder methods….
1. Write methods that always return ‘this’
e.g.
public AccountBuilder withBillingPlan(BillongPlan bp) {
account.setBillingPlan(bp);
return this;
}
this is what allows for chaining (nice pun!).
2.write English-like methods
e.g. asSmallBusiness();
instead of withSegment(AccountSegment.SMALLBUSINESS)
{he made a point that
policy = new PolicyBuilder()
.onAccount(accont)
.withPlan()
.create();
should be the same as
policy = new PolicyBuilder()
.withPlan()
.onAccount(accont)
.create();
I think his point was the creation does happen until create() is called and all data is available
}
Overall:
This was one of the most useful talks I attended this weekend. Perhaps mainly because the project I am currently working on has exactly the kind of monster test-generating class that Ted talked about. I would like to try imlementing the Builder patter approach he advocates. I had a chance to discuss the benefits with Ted after the lecture and he summarised the benefits as this:
- Easy to use default values
- Break the test data generation in to multiple, easier to manage classes rather than one monster one
- The code is much easier to understand, e.g.
Account account = new AccountBuilder()
.withDefaultBillingPlan()
.withStandardPeriod())
is much easier to understand than
Account – new Account(1, 4); etc
Links:
Builder Pattern (Wikipedia)
Subscribe to RSS Feed