RSS Feed Subscribe to RSS Feed

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

  • ThinCrustBuilder, DeepPanBuilder
  • HotSauceBulder, TomatoSuaceBuidler
  • VegToppingsBuilder, MeatToppingsBuilder
  • 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)

    Leave a Reply