Chai is an assertion library that can be used with testing frameworks. Assertion libraries are what you write the tests with. If you're following the links you'll notice that Chai calls itself a "BDD / TDD assertion library". While Test Driven Development is pretty well-known, not so much is known about Behaviour Driven Development.
As a recap with TDD you:
- Write some test for features
- Watch those tests fail (because you haven't written those features yet). If they pass figure out what sorcery is affecting your code before continuing - what's a test if it can't detect failure?
- Implement those features until you get to the point of passing those tests
- Clean up the code, for example refactoring to the original/ideal design while running those test to make sure nothing broke. Rinse and repeat!
With BDD you do the same thing... uhhh what? Of course it's not just that, the Agile Alliance has a lot more to say about their differences. The idea is that BDD describes the behaviour of the feature being implemented, not just its result. The requirements of the project map more closely to the tests you'll write in BDD than in TDD. So while testing we're actually thinking about how the system works. The most salient visible change you'll see is how you write tests, BDD testing tools use more natural language syntax than their TDD counterparts.
Do you even code? (gym prank voice)
These days I've been working with CoffeeScript because it makes me smile :). Since I can't release every bit of code I write (secret agent coder) I decided to do some testing for a Finite State Machine (FSM) acceptor I wrote not too long ago. For those not familiar with FSMs (or FSA, A for Automata) here's a quick primer:
An FSM is a (very) simple model of computation. An FSM has a set of states that it can be in, inputs for the states (alphabet), a start state, a set of accepting/finishing states (where the sequence of inputs are allowed to stop) and transition rules that say how we can move from one state to another.
So we setup a class with all the attributes to model a FSM:
and add a method to accept inputs, in this case an array of the inputs:
accepts: (sequence, currentState=@initialState, trace=false) ->
As you can see there's a function called accept which given an input character and the current state returns the next state or an error if no such state is possible. And here's how painless testing this function with BDD syntax could be:
describe 'FSM accepts', ->
Not bad for the basics right? Describe what you're testing and then get on with it. But wait, there's more! It's a good practice to isolate the environment on every test; in this case, we should have an FSM instance for each test case. By repeatedly initialising the FSM we're sure that any side effects from a previous test would not matter in the current one. Mocha has a simple hook for just that:
With the above code we create a new FSM instance for each test. There are 3 other hooks available to you:
- afterEach - code that runs after every test case
- before - code that runs before any tests begin
- after - code that runs after all tests are completed
Before I move on I'll highlight another code snippet. This test uses TDD syntax instead:
describe 'FSM accept', ->
I wanted to test that the error is thrown when it should. Note that I
wrap the fsm1.accept 2, 'A' in a function (using
those not CoffeeScript savvy). That's because Chai tests if a function
returns an error; just putting fsm1.accept would in essence make Chai
test the result. This seems to be a common thing many people overlook,
do yourself a favour and read them
the gist with all this code.
Don't think that this elegant testing is just for elegant mathematical models. Chai has some useful plugins that could help you test your server HTTP responses. It helped me feel better about my express app's behaviour.
I'm quite happy with my experience of testing with Mocha and Chai. Now remember these are just tools for a software development methodology. Let's try digging deeper into TDD and BDD. I'm particularly interested in BDD as it's newer to me. Here are some links that may be useful to you:
- Introducing BDD - http://dannorth.net/introducing-bdd/
- What's in a Story - http://dannorth.net/whats-in-a-story/
- 3 misconceptions about BDD - http://www.thoughtworks.com/insights/blog/3-misconceptions-about-bdd
My next step is to use the BDD development paradigm from project start to completion, I've only used testing tools after substantial code was already written. The requirements aware approach of BDD appeals to me more than TDD, which has come has come under lots of criticism. David Hansson, creator of Ruby on Rails, has an interesting blog post on why he came off TDD. Would BDD eliminate his problems? Probably not, its TDD roots are the core of his issues. Even so, I'll keep his qualms in mind and try it out myself! In any case, getting our hands dirty with testing would only help our software development processes and results. Happy hacking :).