Welcome to EMC Consulting Blogs Sign in | Join | Help

James Broome Blog

Why BDD works for Agile

 

As I mentioned in my first post (and will probably continue to do so for the unforeseeable future), I recently attended JP Boodhoo's Nothin' But Dot Net training 'boot camp' which has motivated me to start to write about some of the stuff that I learnt and am still trying to make sense of...

The main focus of the course was using Behaviour Driven Design to drive out an application, tests first, from the top down. I was very familiar with the concept of automated unit testing, having used NUnit in the past and using the MSTest tools on my current project. However, I don't think I could truly say that I'd been working 'test first' or that the design of our application was driven out by the test cases. Also, if I were to give a summary report of our automated tests to one of our Business Analysts, I guarantee that it would be met with a blank expression. This makes it tricky to prove that the tests are correct - do they match the requirement and, if this changes, what tests need to change to reflect this?

The whole idea behind BDD is to write tests in plain English, describing the behaviours of the thing that you are testing. When the things that you are testing are written in the common language of the domain, the tests are then describing the behaviour of that domain. The main advantage of this is that the tests reflect the user stories, the business requirements of the application. Careful choice of naming conventions and syntax mean that a unit test report can read like a functional specification. Therefore, it can be read by a non-technical person e.g. the project sponsor, a domain expert, a business analyst and the tests can be validated against the requirements. If a new developer joins the team, they may not know the domain of the application however looking at the tests will immediately give them a very clear idea of the intent of the code.

Let's take the following hypothetical user story for a staff telephone directory application:

"As a user I should be able to view an alphabetical list of the employees so that I can quickly find the person I wish to contact."

 

We may start with writing a test class something like this...

 

using System; 
using System.Collections.Generic;

using Observation = MbUnit.Framework.TestAttribute;
using Context = MbUnit.Framework.TestFixtureAttribute;

[Concerning(typeof(DirectoryTasks))]
public class when_the_directory_tasks_is_told_to_get_all_the_employees_beginning_with_the_letter_A : SpecificationContext
{
private IEnumerable<Employee> results;
private IDirectoryTasks sut; //sut = System Under Test

protected override void establish_context()
{
//Test set up here e.g. mock objects etc...
this.sut = new DirectoryTasks();
}

protected override void because()
{
this.results = this.sut.GetEmployeesBeginningWith("A");
}

[Observation]
public void should_ask_the_telephone_directory_for_all_the_employees_that_begin_with_A()
{
//Interaction based test checking that we are talking to the TelephoneDirectory domain object
}

[Observation]
public void should_return_the_list_of_employees_in_alphabetical_order()
{
//State based test
}
}

 

Note the name of the test class describes the context of the tests. The test methods (observations) are describing the expected behaviour of the thing that we are testing. Note too the Arrange, Act, Assert style of the test. We establish the context for the test, perform the action that we want to test and then assert the correct behaviours of the system under test.

Writing the specification first drives out the need for the following supporting domain classes:

 

public class DirectoryTasks : IDirectoryTasks
{
public IEnumerable<Employee> GetEmployeesBeginningWith(string first_letter)
{
throw new NotImplementedException();
}
}

public interface IDirectoryTasks
{
IEnumerable<Employee> GetEmployeesBeginningWith(string first_letter);
}

public class Employee {}
 

And the test class uses the following utility classes:

 

/// <summary>
/// Base class for all test contexts - enforces the Arrange, Act, Assert style testing
/// </summary>
[Context]
public abstract class SpecificationContext
{
[SetUp]
public void Setup()
{
this.establish_context();
this.because();
}

protected abstract void because();
protected abstract void establish_context();
}

/// <summary>
/// Attribute used in grouping observations
/// when generating test report
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class ConcerningAttribute : Attribute
{
private readonly Type concern;

public ConcerningAttribute(Type concern)
{
this.concern = concern;
}

public Type Concern
{
get { return this.concern; }
}
}
 

Each test class relates to a specific context for a set of tests, which translate as the specification for the system under test. So, you may have more than one test class for each thing that you are testing if there are different test contexts e.g.

when_the_directory_tasks_is_told_to_get_all_the_employees_in_a_department

when_the_directory_tasks_is_told_to_get_all_the_employees_with_a_specific_last_name

And each test context may have multiple tests, or observations, each with different assertions. The assertions themselves can also follow the same BDD coding styles so instead of the typical

Assert.IsTrue();

Assert.AreEqual();

We can use

results.should_only_contain();

result.is_equal_to();

dependency.was_told_to();

Of course, the fluent syntax of the code in the above examples is made possible by the goodness of C# extension methods and some creative thinking about the design and naming of classes. But taking the mantra Red, Green, Refactor, the only thing preventing you from writing truly expressive code is your own creativity. Write the code how you want to use it from the tests, get the code compiling, get the test passing, then refactor the code.

Writing the tests first drives out the code from the top down, whilst writing them in a BDD style syntax gives a meaning to why the tests are there and why that piece of 'real' code should then be written. As I'm already working in an agile environment, collaborating with business analysts and working from user stories, developing features in this way just seems to totally fit. Its only a small step to produce a report of all the tests, parsing the class and method names to give you something like this:

  1. Directory Tasks

When the directory tasks is told to get all the employees beginning with the letter A

  • should ask the telephone directory for all the employees that begin with A
  • should return the list of employees in alphabetical order
  • should return the list of employees in groups of 10

When the directory tasks is told to get all the employees in a department

  • should etc...

 

Look familiar? The tests start to become a direct specification for the system that can be read, understood and validated by any member of the team. They are written in the language of the domain and describe the behaviour of the domain. If the specification changes, or new requirements are added (or missing) it is easy to see which tests and therefore what code needs to change. The code itself becomes the living breathing documentation of the application and this really supports an agile iterative cycle of development, adding new features one at a time.

 

Please note: The example above will compile, but the tests won't yet pass due to the GetEmployeesBeginningWith() method throwing a NotImplementedException. In future posts I will build on this example to flesh out the tests and the associated code.

The test framework used here is MBUnit, so a reference is needed to MBUnit.Framework. Alternatively, the test framework can be replaced by changing the aliases in the using statements at the top of the file.

 

For more info, see http://behaviour-driven.org/

Published Tuesday, July 22, 2008 11:34 PM by james.broome
Filed under: , , ,

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

 

Howard van Rooijen's Blog said:

The ASP.NET (Webforms) proposition holds firm in the light of ASP.NET MVC – its strengths are for large

September 13, 2009 3:49 PM

Leave a Comment

(required) 
(optional)
(required) 
Submit

About james.broome

James is a Technical Consultant at Conchango
Powered by Community Server (Personal Edition), by Telligent Systems