BDD adds one more level of abstraction to the tests. The higher level code (usually in txt) describes what the system tests, the lower level code describes how it tests it. So a BDD framework can use a TDD framework in the lower level code.
This helps a lot by staying DRY. By TDD you can easily end up with "wet" tests containing a lot of code duplication, which make them easy to break by refactoring the code and the tests with it. By BDD you have to modify only the lower abstraction level by refactoring the code, so if the specification does not change, the higher level code won't change.
Btw. this is simple Clean Code, usually it is enough to read the high abstraction level stuff to understand what it does, and dig deeper to the lower abstraction level testing code only if you really need it. This makes (test) code easier to understand in general.
A bad example (because it is too simple):
jasmine TDD style
calculator.add.specs
describe("Calculator: add", function (){
it("should be able to add 2 numbers together", function (){
var total = add(1, 2);
expect(total).toBe(3);
});
it("should be able to add 3 numbers together", function (){
var total = add(1, 2, 3);
expect(total).toBe(6);
});
});
jasmine-cucumber BDD style
calculator.specs
feature('Calculator: add')
.scenario('should be able to add 2 numbers together')
.when('I enter "1"')
.and('I add "2"')
.then('I should get "3"')
.scenario('should be able to add 3 numbers together')
.when('I enter "1"')
.and('I add "2"')
.and('I add "3"')
.then('I should get "6"')
calculator.steps
featureSteps('Calculator:')
.before(function(){
this.values = [];
this.total = null;
})
.when('I enter "(.*)"', function(value){
this.values.push(Number(value));
})
.when('I add "(.*)"', function(value){
this.values.push(Number(value));
})
.then('I should get "(.*)"', function(expectedTotal){
this.total = add.apply(null, this.values);
expect(this.total).toBe(Number(expectedTotal));
});
implementation
calculator.js
function add(){
var args = Array.prototype.slice.call(arguments);
var total = 0;
for (var i in args)
total += args[i];
return total;
}
Now if I rename the add()
function to sum()
I have to change the TDD code in 2 places, while the BDD code only in a single place in the steps file. Ofc. adding one more abstraction level requires more code...