Doing TDD, BDD Style

I've been doing TDD for several years now, and was real involved in IBM's TDD community when I worked there (and yes, they actually did have one ;-).  Over time, my unit test style has evolved from unit testing to find bugs to unit test TDD to define  and spec behavior.

The way I've found most effective to do this is to write my tests in a BDD style.  For those of you not familiar with BDD, it is essentially a way of specifying tests in language that describes the behavior of your system.  There's a somewhat famous initial article on it by Dan North if you have some time.

What's BDD?

BDD usually specified using the "Given, When, Then" language construct, where your "Given" phrases specify initial state, your "When" phrases specify actions on the system, and your "Then" phrases describe expected results.  For a very basic example, a test for someone logging into the system could say,  "Given a user with valid credentials, when he logs into the system, then the welcome page should be displayed".

Its becoming popular in recent years for Acceptance tests to be written in a BDD style, using tools such as Cucumber or Fitnesse.  The holy grail of BDD acceptance test is to have readable artifacts that describe the behavior of the system.  If organized and written well, the output of your test automation should yield a behavioral specification for your product. I think that most implementers still have a way to go on this front.

A similar goal of readability and understanding of behavior should be striven for at the unit test level.  Other developers will likely have to come in after you've written code and have to understand what you've done and make changes.  In this post, I'll try to walk you through how I like to incorporate BDD into my unit tests...

Step 1: Think through what your class should do and describe it with tests

I like to create junit test classes around a specific class.  if I'm creating tons of tests for a specific class, It probably means my class is carrying too much responsibility and violating the Single Responsibility Principle (SRP).  I also like to start my test methods with the word "should" to indicate behavior.  This practice puts the emphasis of the unit test on behavior and makes it more readable to someone who is trying to figure out what the code is supposed to do.

For example, if I want to develop a class that handles clicking a delete button, this is how my unit test my start out (before implementing any production code)

public class DeleteButtonClickHandler {  

@Test public void  
shouldInvokeADeletionOnClick() {  
    assertTrue(false);//fail it at first because no code to execute  
}

Step 2: Break your test into 3 parts using the ARRANGE-ACT-ASSERT pattern and describe each part using GIVEN-WHEN-THEN language

The principle of arrange-act-assert is that you should separate your test into parts that represent setup, execution, and asserting.  This method of breaking up your tests allows for reuse of code, and prevents some test smells.  When you think about it, arrange-act-assert fits quite nicely into the concept of given-when-then.  Given indicates setup of your test, when is the actual act on the system, and then is the expected results.  Therefore, in the delete click example, I can describe the given/when/then as bdd methods as shown below...

@Test public void  
shouldInvokeADeleteOfASelectedItemOnClick() {  
    givenADeleteButtonClickHandler();  
    whenDeleteButtonClicked();  
    thenTheSelectedItemShouldBeDeleted();  
}

Tip: I typically add these methods before actually defining them, thus producing compile errors.  I then let the eclipse easy fix generate the methods for me:-)  It typically do the same thing with productions methods: I define the production methods (and classes) in the testcases by invoking them, and let eclipse create them for me to fix the compile errors.

Step 3: Implement the test

Now that you've created your BDD methods, you can implement them with code that invokes the production system (remembering that you haven't yet written any production code if your are really doing TDD).  In the example we simply invoke the handler and use mock objects to verify that a delete call has been made on the item:

@Test public void  
shouldInvokeADeleteOfASelectedItemOnClick() {  
    givenADeleteButtonClickHandler();  
    givenOneItemSelected();  
    whenDeleteButtonClicked();  
    thenTheSelectedItemShouldBeDeleted();  
}  

private void givenADeleteButtonClickHandler() {  
    this.deleteButtonClickHandler = new DeleteButtonClickHandler(this.mockItemDeleteManager, this.mockSelectionModel);  
}  

private void givenOneItemSelected() {  
    when(this.mockSelectionModel.getSelectedItems()).thenReturn(Arrays.asList(mockItem)); //mockito syntax  
}  

private void whenDeleteButtonClicked() {  
    this.deleteButtonClickHandler.onClick();  
}  

private void thenTheSelectedItemShouldBeDeleted() {  
    verify(this.mockItemDeleteManager).deleteItem(this.mockItem);//mockito  
}

Step 3: Make it work!

Finally, generate/code your production side, and repeat with the next behavior.

I try to use this method whenever I TDD for the following reasons...

  1. It focuses me on behavior when defining tests
  2. It enables me to reuse test code between tests.  My givens and whens are immediately reusable when I put them in methods.
  3. It is easier to read for developers that get the misfortune of coming in after me and have to make updates
  4. Its really cool to see the junit output in eclipse: it goes something like this:
DeleteButtonClickHandler  
    shouldInvokeADeleteOnASelectedItem  
    shouldInvokeABulkDeleteWhenMultipleItemsAreSelected  
    shouldDisplayAnErrorIfDeleteFails  
    shouldNotInvokeDeleteIfNoItemsAreSelected  
    ...

I hope this helps!!!  Let me know what you think.