Running the same mocha test multiple times with different data
Javascriptmocha.jsJavascript Problem Overview
Problem
I have several tests that do the same thing in mocha. This for me, it's duplication, and is the worst thing to do when you want your system to be maintenable.
var exerciseIsPetitionActive = function (expected, dateNow) {
var actual = sut.isPetitionActive(dateNow);
chai.assert.equal(expected, actual);
};
test('test_isPetitionActive_calledWithDateUnderNumSeconds_returnTrue', function () {
exerciseIsPetitionActive(true, new Date('2013-05-21 13:11:34'));
});
test('test_isPetitionActive_calledWithDateGreaterThanNumSeconds_returnFalse', function () {
exerciseIsPetitionActive(false, new Date('2013-05-21 13:12:35'));
});
What do I need
I need a way of collapsing my duplicated mocha tests in only one.
For example, in PhpUnit (and other test frameworks) you have dataProviders.
In phpUnit a dataProvider works this way:
<?php class DataTest extends PHPUnit_Framework_TestCase {
/**
* @dataProvider provider
*/
public function testAdd($a, $b, $c)
{
$this->assertEquals($c, $a + $b);
}
public function provider()
{
return array(
array(0, 0, 0),
array(0, 1, 1),
array(1, 0, 1),
array(1, 1, 3)
);
}
}
The provider in here injects parameters to the test, and the test executes all the cases. Is perfect for duplicated test.
I want to know if in mocha is there something similar, for example, something like this:
var exerciseIsPetitionActive = function (expected, dateNow) {
var actual = sut.isPetitionActive(dateNow);
chai.assert.equal(expected, actual);
};
@usesDataProvider myDataProvider
test('test_isPetitionActive_calledWithParams_returnCorrectAnswer', function (expected, date) {
exerciseIsPetitionActive(expected, date);
});
var myDataProvider = function() {
return {
{true, new Date(..)},
{false, new Date(...)}
};
};
What I have already looked at
There is some tecnique that is called Shared Behaviours . But it does not solve the problem directly with a test suite, it just solve the problem with different components that have duplicated tests.
The Question
Do you know any way to implement dataProviders in mocha?
Javascript Solutions
Solution 1 - Javascript
A basic approach to run the same test with different data is to repeat the test in a loop providing the data:
describe('my tests', function () {
var runs = [
{it: 'options1', options: {...}},
{it: 'options2', options: {...}},
];
before(function () {
...
});
runs.forEach(function (run) {
it('does sth with ' + run.it, function () {
...
});
});
});
before
runs, well, before all it
s in a describe
. If you need to use some of the options in before
, do not include it in the forEach
loop because mocha will first run all before
s and the all it
s, which is probably not wanted. You can either put the whole describe
in the loop:
var runs = [
{it: 'options1', options: {...}},
{it: 'options2', options: {...}},
];
runs.forEach(function (run) {
describe('my tests with ' + run.it, function () {
before(function () {
...
});
it('does sth with ' + run.it, function () {
...
});
});
});
If you do not wish to pollute your tests with multiple describe
s, you can use the controversial module sinon
for this matter:
var sinon = require('sinon');
describe('my tests', function () {
var runs = [
{it: 'options1', options: {...}},
{it: 'options2', options: {...}},
];
// use a stub to return the proper configuration in `beforeEach`
// otherwise `before` is called all times before all `it` calls
var stub = sinon.stub();
runs.forEach(function (run, idx) {
stub.onCall(idx).returns(run);
});
beforeEach(function () {
var run = stub();
// do something with the particular `run.options`
});
runs.forEach(function (run, idx) {
it('does sth with ' + run.it, function () {
sinon.assert.callCount(stub, idx + 1);
...
});
});
});
Sinon feels dirty but is effective. Several aid modules such as leche are based on sinon, but arguably introducing further complexity is not necessary.
Solution 2 - Javascript
Mocha doesn't provide a tool for that, but it is easy to do it yourself. You only need to run the tests inside a loop and give the data to the test function using a closure:
suite("my test suite", function () {
var data = ["foo", "bar", "buzz"];
var testWithData = function (dataItem) {
return function () {
console.log(dataItem);
//Here do your test.
};
};
data.forEach(function (dataItem) {
test("data_provider test", testWithData(dataItem));
});
});
Solution 3 - Javascript
Leche adds that functionality to Mocha. See the announcement and [docs][1].
It is better than simply looping over the tests because, if a test fails, it tells you which data set was involved.
Update:
I didn't like the setup of Leche and haven't managed to get it to work with Karma, so eventually I have extracted the data provider into a [separate file][2].
If you want to use it, just [grab the source][2]. Documentation is available [in the Leche readme][1], and you'll find additional info and usage tips in the file itself.
[1]: https://github.com/box/leche#mocha-data-provider "Leche: Mocha Data Provider"
[2]: https://github.com/hashchange/jquery.isinview/blob/2e80e46723722a8455d0002ea34f4ec94ff4b5b7/spec/helpers/data-provider.js "jQuery.isInView: Source of data-provider.js"
Solution 4 - Javascript
Based on the @Kaizo's answer, here's what I came up with for my test (it's a controller that is getting some parameters from the request) to emulate the data provider in PHPUnit. The getParameters
method is going to receive the request from Express, and then use req.param
to inspect some query parameters, for example, GET /jobs/?page=1&per_page=5
. This also shows how to stub the Express request object.
Hopefully it can help someone as well.
// Core modules.
var assert = require('assert');
// Public modules.
var express = require('express');
var sinon = require('sinon');
// Local modules.
var GetJobs = require(__base + '/resources/jobs/controllers/GetJobs');
/**
* Test suite for the `GetJobs` controller class.
*/
module.exports = {
'GetJobs.getParameters': {
'should parse request parameters for various cases': function () {
// Need to stub the request `param` method; see http://expressjs.com/3x/api.html#req.param
var stub = sinon.stub(express.request, 'param');
var seeds = [
// Expected, page, perPage
[{limit: 10, skip: 0}],
[{limit: 5, skip: 10}, 3, 5]
];
var controller = new GetJobs();
var test = function (expected, page, perPage) {
stub.withArgs('page').returns(page);
stub.withArgs('per_page').returns(perPage);
assert.deepEqual(controller.getParameters(express.request), expected);
};
seeds.forEach(function (seed) {
test.apply({}, seed);
});
}
}
};
The only downside is Mocha doesn't count the actual assertions (like PHPUnit does), it just shows up as one test.
Solution 5 - Javascript
A simpler solution is described below using mocha-testdata library.
Sample solution to the problem.
import * as assert from assert;
import { givenAsync } from mocha-testdata;
suite('My async test suite', function () {
given([0, 0, 0], [0, 1, 1], [1, 0, 1], [1, 1, 3]).test('sum to 6', function (a, b, c) {
assert.strictEqual(a + b + c, 6);
});
});
If you need to test async function calls which is the most common case in node.js app, use givenAsync instead.
import * as assert from assert;
import { givenAsync } from mocha-testdata;
suite('My async test suite', function () {
givenAsync([1, 2, 3], [3, 2, 1]).test('sum to 6', function (done, a, b, c) {
doSomethingAsync(function () {
assert.strictEqual(a + b + c, 6);
done();
});
});
});
Solution 6 - Javascript
I've found mocha-testcheck to be the easiest tool for this. It generates all kinds of data. It will narrow down which input is causing your test to fail.