26. April 2009 23:06 by David in
Last Wednesday I presented a talk on BDD for Skillsmatter. The talk was a great opportunity to drill into my own assumptions about using TDD (Test Driven Development) and TFD (Test First Development).
What is BDD
BDD is a fairly controversial subject as it promotes a Top Down approach to agile software development as opposed to the bottom up approach that is described by Test First Development. For example TFD by its very nature advocates a very tight iterative approach of defining a methods output, implementing code that passes the test and finally refactoring to cleanup the developed code.
This approach is fantastic for developing well understood components. Unfortunately it can become very difficult to build complex systems using TFD. Often team members will write tests AFTER the core business logic has been written (Green, Red, Green or just Green). Sometimes the problem space is not well understood and by using TFD the development effort is pushing towards writing code and not designing the system. The debate also tends to center around the number of tests that have been written as opposed to how useful the developed software actually is. Hence TFD can become biased towards low level coding practice as opposed to system wide design and elegance.
BDD tries to alleviate the situation by swapping the coding process to a classic Top Down or Step Wise Refinement approach. Instead of trying to write production code from the start BDD advocates iteratively defining the API and then once the API is stable writing the actual code that implements the API. The output of TDD are a suit of tests. The output of BDD is a specification for a system’s API and a set of contracts that validate that the API has been implemented.
BDD can be summarised as:
- A formalised template for expressing User Stories
- A methodology that promotes “security” to being first call citizen in the analysis process
- Promoting Domain Driven Design is one of the best methods for building enterprise applications
- Advocating API validation specification based tests as opposed to classic TFD
Key to the success of the BDD approach is the formalisation of the humble User Story.
As a [User/Role]
I want [Behaviour]
so that [I receive benefit]
NBehave in turn takes the BDD template and converts it into a Fluent API
1: var story = new Story("Create portfolio");
2: story.AsA(“Operations team member")
3: .IWant("To create a new portfolio on behalf of a portfolio manager")
4: .SoThat("the portfolio manager can configure the portfolio and the front office can trade");
For each story the developer and analyst creates a suite of scenarios that explore both common and edge cases.
Given [some initial context]
When [an event occurs]
then [ensure some outcomes]
Once in NBehave this becomes
1: story.WithScenario("portfolio does not exist")
2: .Given("portfolio name is $name", "Aggressive Fund")
3: .When("portfolio does not exist in the database")
4: .Then("new portfolio should be created");
At that point mocking allows the developer to iteratively define how the API should respond to external input. Mocks/Stubs are used to allow the developer to experiment with the API. Core components are mocked as opposed to using mocking to emulate dependencies.
In the following example NBehave is used to aid in the design of the IPortfolioRepository and IPortfolioService interface. No production code is executed in the test. Instead the test is used to allow the developer to decide:
- What services should core systems provide
- What properties/methods should core domain entities have
1: string portfolioName = "";
2: Portfolio p = null;
3: var portfolioRepository = MockRepository.GenerateStub<IPortfolioRepository>();
4: var portfolioService = MockRepository.GenerateStub<IPortfolioService>();
5:
6: s.WithScenario("portfolio already exists")
7: .Given("portfolio name is $name", "Aggressive Fund", n => {
8: portfolioName = n;
9: p = new Portfolio { Name = portfolioName };
10: portfolioRepository.
11: Stub(x => x.FindPortfolioByName(portfolioName)).Return(p);
12: portfolioService.
13: Stub(X => X.CreatePortfolio(p)).Throw(new ItemExistsException());
14: })
15:
16: .When("portfolio already exists in database",
17: () => Assert.IsNotNull(
18: portfolioRepository.FindPortfolioByName(portfolioName))
19: )
20:
21: .Then("new portfolio create should fail",
22: () => Assert.Throws<ItemExistsException>(() => portfolioService.CreatePortfolio(p))
23: );
Steps to generate an API using NBehave
- Go through the User story and find all the domain objects
- Create a class for each domain object but don’t add state
- Go through the User story and create a find all the services that are required (Repositories etc)
- Create an interface for each service but don’t add any methods
- Slowly implement the story
- Add domain object properties as required
- Add methods signatures as required
- Use Stubs to explore Inputs/Outputs to the methods