Thursday, August 30, 2012

TDD vs BDD

At a recent Utah Software Craftsmanship group meeting, I was asked to share my experiences using MSpec and explain how TDD is different from BDD. Since I have been using NUnit for years and MSpec since February, I was able to discuss some of the differences in the two styles of testing.

First, A Definition

TDD is Test Driven Development. This means writing a test that fails because the specified functionality doesn't exist, then writing the simplest code that can make the test pass, then refactoring to remove duplication, etc. You repeat this Red-Green-Refactor loop over and over until you have a complete feature.

BDD is Behavior Driven Development. This means creating an executable specification that fails because the feature doesn't exist, then writing the simplest code that can make the spec pass. You repeat this until a release candidate is ready to ship.


At this point, you should stop reading this post and instead go read Dan North's original article on BDD and, if you feel so inclined, my apology for this post. If you choose to continue here, they know that my understanding of BDD at the time I wrote this was fundamentally flawed and you should only use this article as a reference for the differences in the 3 testing tools.


Then again, there is this from Liz Keogh:

They’re called different things
The difference is that one is called Behaviour Driven Development – and some people find that wording useful – and one (or two) is called (Acceptance) Test Driven Development – and some people find that wording useful in a different way.



And that’s it.

so maybe I wasn't so far off.


Those seem pretty similar, right? They are. The key difference is the scope. TDD is a development practice while BDD is a team methodology. In TDD, the developers write the tests while in BDD the automated specifications are created by users or testers (with developers wiring them to the code under test.) For small, co-located, developer-centric teams, TDD and BDD are effectively the same. For a much more detailed discussion, InfoQ sponsored a virtual panel on the topic.

Testing Style

So if NUnit != TDD and MSpec != BDD, what is the difference between these tools? NUnit and MSpec are 2 tools that provide for different styles of developer testing. NUnit promotes the Arrange-Act-Assert style of testing while MSpec requires the Given-When-Then (or Establish context-Because of-It should) style of testing.

Let's look at an example from the bowling game kata:
//NUnit Test
[TestFixture]
public class BowlingGameTests
{
  private Game _game;

  [SetUp]
  public void SetUp()
  {
    _game = new Game();
  }

  [Test]
  public void The_score_for_a_gutter_game_is_0()
  {
    RollMany(20, 0);

    Assert.That(_game.Score() == 0);
 }

  private void RollMany(int times, int pins)
  {
    for (int i = 0; i < times; i++)
    {
      _game.Roll(pins);
    }
  }
}
//MSpec Test
public class With_a_game
{
  Establish context = () => { _game = new Game(); };

  protected static void RollMany(int times, int pins)
  {
    for (int i = 0; i < times; i++)
    {
      _game.Roll(pins);
    }
  }

  protected static Game _game;
}

public class when_rolling_a_gutter_game : With_a_game
{
  Because of = () => RollMany(20, 0);

  It should_score_zero_for_the_game = () => _game.Score().ShouldEqual(0);
}
For a more detailed example, including all the tests in for the kata in both styles, please see this github repository.

How can my team do BDD?

The key to BDD is to get the specifications from the user. In other words, create tests that are not written by developers. This means tests that are not written in a programming language. These tests should be written in a language close to English (or whatever your team speaks.) One of the oldest and best tools for this is FitNesse. The advantages of using FitNesse include
  • it facilitates thinking about features and problems in the language of business rather than the language of code
  • it requires you to focus on data in your tests
  • it can be easily included in a continuous integration environment
  • it includes a wiki for sharing information about the project
  • it requires the creation of fixtures that will help define, and refine, the API
  • it is easily shared with non-developer users

But how do I use FitNesse!?

One easy way to get started is to clone this repository and follow the instructions in the README.md. FitNesse tests consist of 2 parts: the test pages in the wiki and the fixtures that connect the pages to the code under test. In the bddtddfitnesse repo, you will find a file FinalScore.cs in the Fixtures folder. This is the fixture used by the tests.
using System;
using System.Globalization;
using System.Linq;

namespace BowlingKata.Fixtures
{
    public class FinalScore
    {
        private string[] _rolls;

        public void Rolls(string rolls)
        {
            _rolls = rolls.Trim().Split(' ');
        }

        public string Score()
        {
            var game = new Game();
            foreach (int roll in _rolls.Select(x => Convert.ToInt32(x)))
            {
                game.Roll(roll);
            }
            return game.Score().ToString(CultureInfo.CurrentCulture);
        }
    }
}
You can see that the Rolls method takes in a string and converts it to an array of rolls which are used in the Score method. The Score method uses the class under test to generate a score from the input rolls. This is then returned as a string. To see the tests that use this fixture, navigate from the FrontPage to the SuiteBowlingGame and then to the TestScoring page. This page contains some FitNesse specific setup, some text describing the rules and then a test table.
!|final score                                  |
|rolls                                  |score?|
|0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|0     |
|1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1|20    |
|3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3|60    |
|5 5 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|16    |
|10 3 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 |24    |
|10 10 10 10 10 10 10 10 10 10 10 10    |300   |
Each row is a separate test and the value found in the rolls column is passed into the Rolls method of the fixture. The ? on the score column tells FitNesse to query the result of the Score method on the fixture and compare it to the value in the column. When they match, the cell turns green, when they don't match, the cell turns red and the expected and actual are displayed for the user.

So TDD or BDD?

The real answer is both. You need developer tests for the fast feedback and you want user tests to ensure that the features are built to the user specs. We use FitNesse, MSpec (for unit tests), and NUnit (for UI tests) on my team at pluralsight. We try to follow the double loop described in Growing Object-Oriented Software where we write an acceptance test in FitNesse and then unit and integration tests in MSpec. Following this double loop helps us to stay focused and get features done quickly and cleanly.

1 comment:

  1. A few comments

    1.) BDD and TDD are both development tools and team mythologies. It's not one or the other. Scope CAN be a differentiating thing between them, but it's more about the language and style of how your tests are written.

    2.) These things all mean the same thing:
    Arrange, act, assert
    Given, when, then
    Establish, because of, It should.

    It's just syntactic differences, not necessarily different styles of testing. The only other "Style" of testing that I know from a BDD level is record/playback.

    ReplyDelete