Nodejitsu

Save time managing and deploying your node.js app. Code faster with jitsu and npm

Vows custom test runner

About the author

Name
Location
Worldwide
nodejitsu nodejitsu

I recently ported an IRC bot that I wrote from Python to node.js and took the opportunity to try BDD for the first time. My results were very positive. BDD seems to play out a lot like a unittest, but with much more readability.

I used vows, a very well documented BDD library for node.js that has an amazing website and a helpful developer. The principle is that in a given test case, you have a topic. This topic is a specific facet of your program that you'd like to test.

In my example, I test two objects: the pattern, and the pattern list. In prose, its easy to say that "The pattern list is an array that contains objects. You can add something to it by registering something." The tests follow that almost exactly. The topic is the noun, a pattern list. For each clause describing it, you create a new test. There's a test to validate its an array, another to ensure it contains objects, and a third to check that when you register something, the list grows. All fairly straight forward.

The product of this is a very simple list of dots followed by the time it took to run and how many tests passed. You can pass some arguments, if you're using the vows command, and you'll get nice RSpec output. Very fancy.

What if we wanted something different? Perhaps a bit retro? Look no further than asciimo, which makes it quite easy to get ascii art with little effort. You can even throw in some color for fun.

var asciimo = require('/path/to/asciimo').Figlet,  
    colors = require('/path/to/colors');

asciimo.write('Test', 'drpepper', function(art) {  
  console.log(art.blue);
});

Tying this into vows turns out not to be terribly difficult either. After some initial trouble getting custom test reporters registered, I was pointed the right way by cloudhead on #node.js, the author of vows. Turns out I was using an non-public API along with not exporting my test suite into a form that vows understood. Custom reporters only need to implement a report method which takes two arguments. From what I could tell, the second method wasn't used (at least not for standard test running), but the first argument provides a wealth of information about the test suite.

The first argument is a list whose first element is a string which is followed by an optional second item. The string seems to represent which portion of BDD's tree based structure you're on. The second element in the array is some structure describing the first argument. In the example of an actual test, it shows the test name and whether it passed (was honored) or failed, among other things. Some examples include:

[ 'subject', 'imbot patterns' ]
[ 'context', 'The pattern list' ]
[ 'vow'
   , { title: 'is an array'
     , context: 'The pattern list'
     , status: 'honored'
     , exception: null
     }]
[ 'end' ]
[ 'finish'
   , { honored: 6
     , broken: 0
     , errored: 0
     , pending: 0
     , total: 6
     , time: 0.007
     }]

Based on this, we really only need to pay attention for the 'finish' message to be passed. For instance, a simple custom reporter might look like:

var sys = require('sys');  
var colors = require('../../colors.js/colors');  
var asciimo = require('../../asciimo/lib/asciimo').Figlet;  
var report_font = 'drpepper';

this.report = function (data, s) {  
  if (data[0] == 'vow') {
    sys.print('.');
  }
  if (data[0] == 'finish') {
    sys.print('\n'); // clear a newline
    var result = 'failed';
    if (data[1].broken == 0 && data[1].errored == 0) {
      result = 'passed';
    }
    asciimo.write(result, report_font, function (art) {
        if (result  == 'passed')
          sys.puts(art.green);
        else
          sys.puts(art.red);
        var output = '';
        output += 'Passed: ' + data[1].honored + ' \n';
        output += 'Failed: ' + data[1].broken + ' \n';
        output += 'Errored: ' + data[1].errored + ' \n';
        output += 'in ' + data[1].time + 'sec';
        sys.puts(output);
    });
  }
};

And the result of that is an ascii art of the words passed or failed in green or red respectively, followed by how many things passed and failed and the total execution time. Not too shabby for under 30 lines of code. For the sake of another example, below is another simple example using the say.js and play.js libraries.

// using say.js
var say = require('say');

this.report = function (data, s) {  
  if (data[0] == 'finish') {
      var result = 'You had ' + data[1].broken +' broken, ' 
          + data[1].errored + ' errored, and '
          + data[1].honored + ' passing tests.';
    say.speak('Alex', result);
  }
};

// using play.js
var play = require('play');

this.report = function (data, s) {  
    if (data[0] == 'finish') {
        if (data[1].broken + data[1].errored > 0) {
            play('/path/to/sad_trombone.wav');
        } else {
            play('/path/to/success.wav');
        }
  }
};