BDD Antipattern: Specs with concealed intent

A great benefit of Behaviour Driven Development (BDD) is that the unit tests become the primary way of documenting the functionality of a system.  In fact, this why in BDD, “tests” are usually referred to as “specifications” or “specs”.  However, developers need to ensure that the intent of their spec is clearly evident and revealed.

Consider, for a moment, the following spec:

Given my test data
When my analysis is run
Then the results should be correct

It’s impossible to draw any conclusions from this about the functionality of the system, without being able to see the implementation details of this spec.  Let’s suppose that the step implementation details are as follows:

[ActionSteps]
public class DataAnlaysisSteps
{
    private AverageCalculator _calculator;

    [BeforeScenario]
    public void SetupContext()
    {
        _calculator = new AverageCalculator();
    }

    [Given("my test data")]
    public void InsertTestData()
    {
        _calculator.RecordValue(10);
        _calculator.RecordValue(20);
    }

    [When("my analysis is run")]
    public void Analyse()
    {
        _calculator.PerformAnalysis();
    }

    [Then("the results should be correct")]
    public void CheckResults()
    {
        Assert.AreEqual(15, _calculator.Result);
    }
}

If you read through this in detail, then you can probably guess that we are testing some code that computes average values.  Now there is no reason that our spec cannot make this clear:

Given my test data
When the average value is computed
Then the average value should be computed correctly

This is certainly an improvement, albeit a small one.  Language in BDD specs is quite important (as I’ve mentioned previously).  However, it’s impossible to extend this spec to test other datasets and it’s still not entirely clear what we mean by “computed correctly”.  Let’s address this:

Given a value of 10
And a value of 20
When the average value is computed
Then it should be 15

With this spec, there is no doubt about its purpose and the expected behaviour of the underlying system.  It’s also easy to see how we could add more specs to validate the behaviour with other datasets.  For those who are interested, the step implementation details are as follows:

[ActionSteps]
public class DataAnlaysisSteps
{
    private AverageCalculator _calculator;

    [BeforeScenario]
    public void SetupContext()
    {
        _calculator = new AverageCalculator();
    }

    [Given("a value of $n")]
    public void RecordData(int n)
    {
        _calculator.RecordValue(n);
    }
    
    [When("the average value is computed")]
    public void Analyse()
    {
        _calculator.PerformAnalysis();
    }

    [Then("it should be $result")]
    public void CheckResult(int result)
    {
        Assert.AreEqual(result, _calculator.Result);
    }
}
April 28 2011
blog comments powered by Disqus