November 22, 2010

Behavior Driven Development with JUnit 4

Originally published 14 Dec 2006

JUnit 4 makes Behavior Driven Development (BDD) style testing (or specification) easier.

Let's quickly look at an example, the idea stolen from one of David Chelimsky's blog entries about specifying the behavior of a stack. We're focused here on the specification of an empty stack:

// notice the class name specifies the context
public class EmptyStack {
    private Stack stack = null;
    
    @Before
    public void setUp() {
        // set up the context
        stack = new Stack();
    }

// notice the name focuses on the context
    @Test
    public void shouldBeEmpty() {
        assertTrue("not empty", stack.isEmpty());
    }

    @Test(expected=EmptyStackException.class)
    public void shouldComplainOnPeek() {
        stack.peek();
    }
    
    
    // more specification focused methods
}

The point here is that you can get many of the benefits of BDD (a focus on specification rather than testing) using the familiar JUnit framework.

Now, if you're a hardcore BDD'er, then you might complain that you still have to use a test-centric vocabulary. You still need those Test annotations and method calls like assertEquals rather than Dave Astels' preferred shouldEquals calls.

Also, from the legacy side of the fence, you lose the convention of method names starting with testSomething and class names ending with Test. It's sometimes hard to let go of that if you've been writing tests for a long time and it's super clear to spot those test methods and test classes if you're following a naming convention. Furthermore, Ruby on Rails has taught us that convention is a good thing. The test method naming convention doesn't really bother me so much. The @Test annotation makes things clear enough and shouldSomething really gets us focused on specification. However, I haven't let go of ending the class name with Test. Maybe it's more because Ant can pick those tests up easier if there's a standard naming convention.

The last negative I can think of is that of consistency. Having a mix of old style test centric tests and BDD style tests could bother some.

So, let's be honest. What am I really doing? Well, I am incorporating more and more BDD into my work. However, I still shy away from creating new classes. The BDD style really lends itself to many test classes (contexts) per class under test. In the case of Stack, as David Chelimsky points out, you'd also need AlmostEmptyStack, AlmostFullStack, and FullStack classes to fully specify the behavior. I just can't commit myself to writing all those. But I am focusing more on the set up of a context and the specification methods of that context. I just may cheat a little and combine contexts into a single class. So perhaps in the Stack example, I'd combine the empty and almost empty stack contexts under one test class. You know, I'd set up an empty Stack in setUp and for the almost empty context, I'd do a little more set up (push something) in the appropriate shouldSomething methods to make it almost empty.

I also revert back to legacy style testing sometimes. This is usually when I'm modifying an existing, old style test. It's just simpler and faster, and typically the context isn't set up too well for the BDD style.

So my advice is to use the right tool for the job. Use BDD style when that makes things clearer and you want to focus on specification. Use test centric style when that's easier. Try to do a better job of focusing more on specification and less on verification, especially when you're adding new behavior.

No comments:

Post a Comment