TDD and BDD
These are two terms that get thrown around a lot, interchangeably even, but what actually is the difference between Test Driven Development and Behaviour Driven Development? Firstly, lets define what TDD and BDD are:
TDD: Write a test and watch it fail, then write enough code to make the test pass. Optionally, you can then go back and refactor your code. The aim here is to write the minimum code required to make the test pass. Following TDD makes it possible to have a very high test-coverage as well as promoting a simpler design and confidence that the system works as expected.
BDD: BDD is very similar to TDD, but there is a shift from thinking in Tests to thinking in Behaviour. A common problem with TDD is they rely too much on implementation detail and BDD looks to eradicate that. You want to focus on solely on the inputs and outputs, written in clear English which all parties (software developers, QA, domain experts etc.) can understand. The benefit is that it offers a more precise and organized conversation between developers and domain experts.
So the difference between the two concepts is a little fuzzy. I agree with Dan North’s definition of BDD: “test-driven development done right”. Personally, I am more in favour of the BDD approach as I feel you get all the benefits of TDD, while making your tests easy to understand and in turn, more maintainable.
For more on BDD, I would refer you to Dan North’s article on BDD here.
SpecFlow
An excellent tool for BDD is SpecFlow, available for .Net, combines Gherkin specification language to create an efficient BDD testing solution. An example test file would look like the following:
Feature: Calculator In order to avoid silly mistakes As a math idiot I want to be told the sum of two numbers @mytag Scenario: Add two numbers Given I have entered 50 into the calculator And I have also entered 70 into the calculator When I press add Then the result should be 120 on the screen
A SpecFlow test generally consists of 3 steps:
Given: setup of the test data.
When: the action you are performing which is being tested.
Then: the assertion to make sure your functionality is correct.
Of course, you can have many combinations of the 3 main steps, depending on your testing structure. Each step is bound to a method in your bindings file in C#. I have found that these can become very convoluted overtime, making it difficult to maintain, but thankfully, SpecFlow allows you to go directly to the step like you would normally navigate to a definition of a method, as well as automatically generating step binding skeletons for you. SpecFlow is also provides auto-complete for when writing new tests to make it easier to find existing steps. Here are the step bindings for our example test:
[Binding]
public class CalculatorSteps {
private int result {
get;
set;
}
private Calculator calculator = new Calculator();
[Given(@ "I have entered (.*) into the calculator")] public void GivenIHaveEnteredIntoTheCalculator(int number) {
calculator.FirstNumber = number;
} [Given(@ "I have also entered (.*) into the calculator")] public void GivenIHaveAlsoEnteredIntoTheCalculator(int number) {
calculator.SecondNumber = number;
} [When(@ "I press add")] public void WhenIPressAdd() {
result = calculator.Add();
} [Then(@ "the result should be (.*) on the screen")] public void ThenTheResultShouldBeOnTheScreen(int expectedResult) {
Assert.AreEqual(expectedResult, result);
}
}
This is a very basic SpecFlow test, but it is much more powerful. Instead of providing parameters within the step name, you can provide a table if you have a larger set of data:
| Field | Value | | Name | John Galt | | Birthdate |
| 2/2/1902 | | HeightInInches | 72 | | BankAccountBalance | 1234.56 |
This can be very useful for your setup of tests. SpecFlow can then automatically map the provided data into a specified type, for the fields provided:
[Given(@ "Given I entered the following data into the new account form:")] public void x(Table table) {
var account = table.CreateInstance(); // account.Name will equal "John Galt", HeightInInches will equal 72, etc. }
Although this is very good, I would recommend using SpecFlow.Assist.Dynamicm, which is useful when you have a large number of parameters for your step, which you don't want to map to a type.
I have been using SpecFlow for several years now, and I am a big fan. Having used MSpec, NUnit and MSTest to name a few, this is my favourite due to the readability of the tests; In a feature file, you can read the test as simple English. You don’t care about mocked objects or database contexts, all you want to know is what the actual functionality currently is, and most importantly, the domain experts (your client) will be able to understand it. Of course you will need to ensure each of your steps are correct and you have a rigid structure, but after that, tests are easier to write and amend. I would thoroughly recommend trying SpecFlow in your next project.
Comments