Monday, April 4, 2011

Getting started with BDD using NBehave

What is BDD? BDD stands for Behavior Driven Development and at its core is about implementing an application by describing its behavior from the perspective of its stakeholders. It is an agile software development methodology that started as an offshoot of TDD. However, in my opinion it is better suited to software delivery than TDD is. The reason is that TDD is great as a code design methodology, but there is a disconnect between code design and stakeholder value. When practicing TDD, developers typically produce cleaner and more maintainable code. However, that elegant code may not deliver what the stakeholders wanted.

BDD also allows non technical users, like BAs and business users to specify system behavior in a non technical, natural language. Tools like NBehave, allow the developers to verify and demonstrate that the system delivers what the stakeholders expect.

How do we write a scenario? Scenarios typically follow the Given/When/Then structure. So for example:

Scenario: User logs in
Given that I am not logged in
And I have an account
When I enter my username and password
Then I should see “Welcome Vadim”

How would we use NBehave to verify that the system does what the scenario says? First thing's first download the latest NBehave build and extract the contents of the zip file.
There are 2 versions of the DLLs, one for .Net 3.5 and one for .Net 4.0.

Create a project for your tests and add a reference to NBehave.Narrator.Framework.dll. Also add a reference to your favorite testing framework (like NUnit or xUnit) and the appropriate NBehave.Spec.<framework>.dll. Currently NBehave comes with extension methods for NUnit, xUnit, MbUnit, and MSTest. These methods are simply wrappers around Assert statements, that allow a more natural language feel when writing your code. So if you're using something like MSpec, which already has Should<DoSomething> type assertions, then you don't need to worry about this last step.

Next, create a features directory inside your project. This is where you'll store your feature and scenario files. Let's pretend we're writing a calculator application. Add a file to the features folder called calculator_turns_on.feature. Time to write our scenario.

Scenario: I turn on the calculator
Given the calculator is off
When I press the on button
Then the calculator should display 0

Let's add a folder called steps. This is where we're going to put our tests. Also create a code file and call it CalculatorTurnsOn.cs. Let's start with a skeleton test:

public class CalculatorTurnsOn

Compile the solution then run the NBehave-Console like so

NBehave-Console.exe bin\Debug\NBehaveIntro.dll /sf=features\calculator_turns_on.feature

You should see all results yellow with a PENDING tag next to them. These aren't failures, but we know that we have to implement this scenario. Let's create a Calculator class.

public class Calculator
    public float Display { get; set; }

Next we'll flesh out our test.

public class CalculatorTurnsOn
    private Calculator calculator;

    [Given("the calculator is off")]
    public void CalculatorOff()
        calculator = null;

    [When("I press the on button")]
    public void TurnCalculatorOn()
        calculator = new Calculator();

    [Then("the calculator should display 0")]
    public void CalculatorDisplayIs0()

Compile and run the command again. Now the tests all pass.

Note how the attributes are decorated with the same sentences that were written in our scenario. That's how NBehave knows how the scenarios are mapped. You can decorate a method with multiple Given, When, and Then attributes and you can have any number of methods in a given ActionSteps class. Furthermore, you can have regular expressions or string tokens in the description as well. For example if we had the following scenario:

Scenario: I can add two numbers
Given numbers [one] and [two]
When I add them
Then the sum should be [sum]

|1  |2  |3  |
|2  |2  |4  |
|10 |35 |45 |

Our attribute decorations might look like:
[Given("numbers $one and $two")]
public void TwoNumbers(float one, float two)
[Then("the sum should be $sum")]
public void VerifySum(float sum)

One last note, the scenario above also contained an example table. This table allows us to write one scenario with multiple inputs and outputs to test. This saves us from having to write repetitive scenarios when warranted.

For further info see the following links:

1 comment: