I recently spoke at UtahJS and presented on the history of BDD and some of my experiments.
The presentation can be found here with the source code on GitHub. It covers the invention of TDD, evolution into BDD, the various BDD flavors, and all JS testing options currently available.
Experiments
Skeleton / Crossbones
GitHub: https://github.com/Ensighten/crossbones
For a while, I have been experimenting with BDD. At first, I wanted a cross-framework test runner from vows to mocha. This was initially called Skeleton, later renamed to Crossbones and was successfully implemented in the Sauron test suite.
This was great but I wanted something better. Crossbones required writing the tests in their own framework which was similar to vows but not vanilla vows (exporting was weird).
// Create a new test suite
var suite = new Skeleton('Sauron.js');
// Add in a test batch -- this is for a global mediator
suite.addBatch({
'Sauron': {
'can emit events': function () {
Sauron.voice('hello');
},
'can set up functions to subscribe to events': function () {
var works = false;
Sauron.on('basicOn', function () {
works = true;
});
Sauron.voice('basicOn');
assert(works);
},
'can unsubscribe functions from events': function () {
var count = 0;
function basicOff() {
count += 1;
}
Sauron.on('basicOff', basicOff);
Sauron.voice('basicOff');
Sauron.off('basicOff', basicOff);
Sauron.voice('basicOff');
assert(count === 1);
}
}
});
// Export the suite to mocha
suite.exportTo('Mocha');
// and run it
var runner = mocha.run();
Sculptor
GitHub: https://github.com/twolfson/sculptor
A long while after Crossbones and its ultimate neglect, I thought about writing a framework for cross-compiling test suites. The key reason here was to flatten test suites into TDD-compatible flavors for [testling][testling].
I began hacking on this and got quite far (it works with wrapping vows into mocha tests) but soon realized that compiling a test suite into another framework's test suite was a total mindfuck.
doubleshot
GitHub: https://github.com/twolfson/doubleshot
After considering Sculptor as an over-engineered project in attempt to find a holy grail.
I have begun to realize all of my "holy grails" are usually over-engineered items that I throw away a month later.
I decided to start on something much simpler and finer focus. I realized that saying describe
versus typing out topic
was a silly semantics issue. On previous projects, I wrote out a specification for my test before writing the tests themselves.
I realized this was awesome and always had the side thought that the specification would become lost once I fill out the tests due to code bloat. I wanted a cleaner way in the same loose language to keep them around.
Upon this next iteration, I tried out splitting the outline from the content and fell in love with the latest format. doubleshot was born.
// outline.json
{
"One": {
"is equal to one": true
}
};
// test/content.js
{
'One': function () {
this.one = 1;
},
'is equal to one': function () {
assert.strictEqual(this.one, 1);
}
}
// Runs test as
describe('One', function () {
before(function () {
this.one = 1;
});
it('is equal to one', function () {
assert.strictEqual(this.one, 1);
});
});
Shortly after writing, I discovered cool functionality I could build in (e.g. aliasing
and chaining
of methods).
// outline.json
{
"One plus two": {
"is equal to three": true
}
}
// content.js
{
// Breaks 'One plus two' action into 2 actions
'One plus two': ['One', 'plus two'],
'One': function () {
this.sum = 1;
},
'plus two': function () {
this.sum += 2;
},
// Alias 'is equal to three' as 'equals three'
'is equal to three': 'equals three',
'equals three': function () {
assert.strictEqual(this.sum, 3);
}
}
// Runs test as
describe('One plus two', function () {
before(function () {
// These are contained inside functions but have the same effect
this.sum = 1;
this.sum += 2;
});
it('is equal to three', function () {
assert.strictEqual(this.sum, 3);
});
});
I am quite satisfied with the current result. There are a few kinks to work out (e.g. JSON is unordered and can cause issues). I might try out yamlish (a subset with only objects and arrays), a properietary nested interface, or even markdown.
yamlish (same ordering issues as JSON)
A strawberry:
when washed:
- is red
- is tasty
Proprietary line-delimited markup
A strawberry
when washed
is red
is tasty
Markdown, ordered since it compiles to XML
# A strawberry
## when washed
- is red
- is tasty
# A strawberry
## when washed
### is red
### is tasty