Asynchronous Test Loops With Mocha Using it-each
Nowadays people are using Mocha more and more for testing their JavaScript/NodeJS code. One major limitation of Mocha is that there is no real way to loop tests asynchronously and maintain the ability to keep track of what's happening (such as realising which iteration a test fails on). Due to this, I created a module to allow the use of asynchronous test loops with Mocha. Currently, it's trivial to loop tests in Mocha provided your code is synchronous - literally just a typical JavaScript loop. Consider the synchronous example code below (I know, it can never fail):
describe('A synchronous loop of tests', function() {
it('tests squared numbers', function() {
for (var i = 1; i <= 10; i++) {
if ((i * i) / i != i) {
throw new Error('This will never happen');
}
}
});
});
In this case, the loop will run just fine, but what happens if you want to run a sequence of URL calls with various validations as part of a test? Say you have 20 APIs you want to call in your tests to check for a 200 response code, you don't want to write out 20 individual it()
in order to do your testing - rather you'd want to create an array of your API paths, and call them in a loop. Due to the nature of this, it is possible to do this in Mocha by putting an async loop inside the Mocha it
block, however you wouldn't be able to figure out which element of your loop the test failed on, without additional logging.
This is where it.each comes in. This module gives you the ability to dynamically display your test title based on the current iteration in your loop. Full examples can be found in the repository linked above, with dynamic titles which will show you which test failed correctly. Here is a quick example using the API example:
var https = require('https');
var APIs = [ 'last-day', 'last-week', 'last-month' ];
describe('Asynchronous loop testing', function() {
it.each(APIs, 'Testing code of %s', ['element'], function(element, next) {
var options = {
hostname: 'api.npmjs.org',
slug: '/downloads/point/' + element + '/it-each'
};
https.get(options, function(res) {
// Check res code
if(res.statusCode != 200){
throw new Error('Found none 200 response code at ' + element + '!');
}
// annoyingly you can't fire 'end' without 'data'
res.on('data', new Function()).on('end', next);
});
});
});
The above code just calls different ranges on the NPM stats API and checks the response code. You should always call the next callback inside the on('end')
event. Unfortunately, you need to have on('data')
specified for on('end')
to fire.
Dynamic Titles
Conveniently, the title of the Mocha loop will change depending on the iteration. For example, in the first iteration, the test title will state:
Testing code of last-day
which will then change as we move through the loop:
Testing code of last-week
This means that if the code above were to fail on the second element ('last-week'), your Mocha output would read:
Asynchronous loop testing
1) Testing code of last-week
Full instructions on how to require and use it.each are inside the README.md in the repo, and the module can be installed via NPM.