November 22, 2010

Essence Over Ceremony in Unit Testing

Originally published 9 Aug 2008

There's been some talk recently about essence and ceremony, particularly regarding JVM programming languages. My first remembrance of this discussion was reading this blog entry from Stu Halloway.  Java is a ceremonious language because there's a lot of extra, requried typing that blurs the essence of what you're trying to communicate in the code.

I want to talk about essence vs. ceremony in unit testing.

If I give you this:

OrderTest
    testCalculatePrice
        1 20.00
        2  5.00
          30.00

Can you tell what this is about? This is a specification of the calculatePrice() method on an Order object, where there are two LineItem objects and the expected price is $30.00. Did you figure that out without the explanation? This is essence.

Instead, what you often see is something like this:

public class OrderTest {
    @Test
    public void testCalculatePrice() {
        Order order = new Order();
        Product product1 = new Product(20.00);
        LineItem lineItem1 = new LineItem(1, product1);
        order.add(lineItem1);
        Product product2 = new Product(5.00);
        LineItem lineItem2 = new LineItem(2, product2);
        order.add(lineItem2);
        assertEquals(30.00, order.calculatePrice());
    }
}

Here, you're blinded with irrelevant details that obscure what this test is all about. The essence of the specification is buried in details. What you want is clarity at this level.

You can factor out the obscurity with something like this:

public class OrderTest {
    private Order order = new Order();

    @Test
    public void testCalculatePrice() {
        lineItem(1, 20.00);
        lineItem(2, 5.00);
        expectPrice(30.00);
    }
}

That's gets us about as close as we can in Java.

A consequence of this approach is that you have more methods overall, but I've always felt that clarity trumps in specifications. Others have agreed.

What's interesting with these helper methods is that I can change how I implement them and not change the higher level, essence methods at all. For example, in lineItem() I can inject real production objects (as I did in the original example with LineItem and Product), or I can inject a mock or stub LineItem.

So my suggestion is when you are specifying these high level methods, attempt to only show the essence of the test and factor out the ceremony.

No comments:

Post a Comment