I am writing a test where I need to loop over the results of an async api call and dynamically make mocha 'Its' to test each iteration of the response. I found some other related answers which got me started. Here is what I have tried so far.
function getter(uri) {
return new Promise(function(resolve, reject) {
request({
method: 'GET',
json: true,
uri: uri
}, function(error, response, body) {
if (response.statusCode == 200) {
resolve(body);
} else {
reject(error);
}
});
});
}
describe('This works', function() {
it('works', function(done) {
getter('myapi_that_returns_an_array').then(function(r) {
r.should.not.be.empty;
done();
}).catch(function(err) {
done(err);
});
});
});
describe('Why not this one', function() {
getter('myapi_that_returns_an_array').each(function(r) {
it('should make a test', function(done) {
r.should.not.be.empty;
done();
});
});
});
I tried to just wrap a simple array in a promise and feed that to my test and it works! So I cannot figure out why the api call I make does not work the same way.
function simple_test() {
return new Promise(function (resolve, reject) {
resolve([ [1, 2, 3, 4], [2, 3, 4, 1], [3, 4, 2, 1], [4, 1, 2, 3] ]);
});
}
describe('But this one works', function() {
two().each(function(r) {
it('is a test', function(done) {
r.should.not.be.empty();
done();
});
});
});
I tried to add a timeout to simple_test to make it act like the api call - the result is the same as the api call. Is there a way to do this with Mocha? It looks like the describe is executing before waiting for the promise to resolve.
The version of your test that tries to generate tests by calling it
inside getter.each
cannot work because Mocha has no provisions for generating tests asynchronously which is what you are trying to do. As I've explained here:
What you get has to do with how Mocha discovers your test. Basically Mocha does this:
Read all your test files and execute them. The callbacks passed to describe
are executed right away. The callbacks passed to it
and to the hooks (before, beforeEach
, etc.) are recorded for later execution.
Mocha executes what it has recorded for later execution (according to some sensible order which is not important here).
The problem with trying to generate tests asynchronously is that by the time the asynchronous code executes you're out of your describe
block, and the tests are ignored. There is no way to tell Mocha to wait for an asynchronous operation before it considers a describe
block over.
I think the problem is that you must define the tests synchronously, although each individual test may complete asynchronously. You can dynamically define it()
blocks from static data because these tests are defined before the describe()
call returns.
I don't fully understand why the test works asynchronously with little or no timeout interval, but my experiments suggest that it()
must be called at least once before describe()
returns for tests to be recognized in the output. With a 1 millisecond timeout, I saw tests described in the parent block.
Using before()
blocks suffers from the same problem. before()
could wait for the promised array to resolve, but without any it()
tests statically defined, before()
will never get run.
The simple but undesirable alternative would be to have a single it()
block test all of the data returned by your service. Another would be to use the results of your web service to first dynamically generate a mocha test file, then run mocha against it.
var assert = require("assert");
var Promise = require("bluebird");
var testData = [
{ "name": "Test 1", "value": true },
{ "name": "Test 2", "value": false },
{ "name": "Test 3", "value": true }
];
function getTestData(timeout) {
return new Promise(function (resolve, reject) {
if (timeout) {
setTimeout(function () {
resolve(testData);
}, timeout);
} else {
resolve(testData);
}
});
}
describe("Dynamicly Generating Tests", function() {
describe("A: Promised Array, no timeout - Works", function () {
getTestData().each(function (test) {
it("A: " + test.name, function () {
assert.ok(test.value);
});
});
});
describe("B: Promised Array, short timeout - Works?", function () {
getTestData(1).then(function (testData) {
testData.forEach(function (test) {
it("B:" + test.name, function () {
assert.ok(test.value);
});
});
});
});
describe("C: Promised Array, timeout, single it() - Works!", function () {
it("C: Has all correct values", function () {
return getTestData(1000).each(function (test) {
assert.ok(test.value, test.name);
});
})
});
});