I have a section of code that is using node-sync like so:
function funcA() {
return new Promise(function(resolve, reject) {
Sync(function () {
return funcB.sync();
}, function (err, result) {
if(err) {
reject(err);
} else {
resolve(result);
}
});
}
This code is tested using mocha+chai:
it("should return array", function() {
return funcA().then(function(result) {
expect(result).to.be.an.instanceof(Array);
});
});
It worked just fine couple of months ago, but now this test always times out:
Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.
What I've tried so far:
using done() instead of returning a promise
replacing node-sync with synchronize.js
increasing timeout
What I found out, is that expect(... part of this test is, in fact, being called, but only after mocha kills the test. No matter what timeout interval is currently set, expect(.. is being called always ~20 milliseconds after I get Error: timeout message.
I fixed the issue by adding setInterval(function(){}, 10) on the top of the test file. I'd like to know why this worked and if there is some better way to fix that?
[EDIT] It looks like this is a node-version specific issue. Test fails on 0.12.4 but runs correctly on 0.10.38 .
[EDIT] Actual code is available here.
Based on your code, I am assuming that your funcB function is running code in a synchronous way.
So when I create funcB in this way:
function funcB() {
return [1, 2, 3];
}
And run the test, Mocha shows an error:
Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.
But if I convert the funcB in asynchronus function as this:
function funcB(cb) {
process.nextTick(function () {
cb(null, [1, 2, 3]);
});
}
Mocha runs the test without any problem:
✓ should return an array
So my complete code that runs ok (the funcB commented is the one that will cause the error) is this:
// install dependencies
// npm install promise
// npm install sync
var Promise = require('promise');
var assert = require('assert');
var Sync = require('sync');
function funcA() {
return new Promise(function (resolve, reject) {
Sync(function () {
return funcB.sync();
}, function (err, result) {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
}
// function funcB() {
// return [1, 2, 3];
// }
function funcB(cb) {
process.nextTick(function () {
cb(null, [1, 2, 3]);
});
}
it("should return an array", function(done) {
return funcA().then(
function (result) {
console.log(result);
assert.equal(Array.isArray(result), true);
done();
}
);
});
So I am opinion I think that the misuse of the sync method (using it on synchronous functions) created by sync library is the one that is causing this problem.
You may just be missing the done() callback:
it("should return array", function(done) {
funcA().then(function(result) {
expect(result).to.be.an.instanceof(Array);
done();
});
});
http://mochajs.org/#asynchronous-code
You can use a timeout parameter for mocha executable.
For example, if you want a 500 milliseconds timeout, just change your package.json to:
"scripts": {
"test": "mocha specs --timeout 500 --require specs/helpers/chai.js"
},
And maybe your promise is being rejected, so you have to call done using catch method.
it("should return array", function(done) {
return funcA().then(function(result) {
expect(result).to.be.an.instanceof(Array);
done();
}).catch(done);
});
This would also help you debug eventual errors that might happen in your promise code.
Related
Test cases(Test1, Test2) execute before promise get the data. This is the file mockExecution.js
describe('AC 1: This is suite one', ()=>
{
before((done)=>
{
promiseResp.then((data) => {
console.log("i am in the promise");
responseData = data;
process.exit(0);
}, (err) => {
console.log('promiseResp.err', err);
process.exit(1);
})
done();
})
it('Test1', (done)=>
{
expect(responseData.measure.abc).not.toBe(responseData.measure_list.abc);
done();
});
it('Test2', (done)=>
{
expect(responseData.measure.abc).not.toBe(responseData.measure_list.abc);
done();
});
});
PromiseResp inside the Before block doesn't execute. Therefore "responseData" variable doesn't have data and it throws test case failed. I guess there is some asynchronous time issue, but don't know how to resolve it and also where do i put this "process.exit(0)". Below is the actual output:
AC 1: This is suite one
I am in the before
1) Test1
2) Test2
0 passing (7ms)
2 failing
1) AC 1: This is suite one
Test1:
TypeError: Cannot read property 'measure' of undefined
at Context.it (QA/mockExecution.js:160:29)
2) AC 1: This is suite one
Test2:
TypeError: Cannot read property 'measure' of undefined
at Context.it (QA/mockExecution.js:167:29)
[process business logic and prints some logs here, i can't paste here]
finished analyzing all records
i am in the promise
npm ERR! Test failed. See above for more details.
I am expecting output in the following sequence:
[process business logic and prints some logs here, I can't paste here]
finished analyzing all records
AC 1: This is suite one
I am in the before
I am in the promise
1) Test1 passed
2) Test2 paseed
You need to call done within your then & after you actually
assigned responseData = data:
before((done) => {
promiseResp.then((data) => {
responseData = data;
// Promise has resolved. Calling `done` to proceed to the `it` tests.
done();
})
.catch((err) => {
// Calling `done` with a truthy `err` argument, in case
// the promise fails/rejects, to fail-early the test suite.
done(err);
})
})
otherwise before ends prematurely and proceeds to the next tests, before the promise actually resolves and assigns your responseData variable.
Here's a working example using the before hook:
const expect = require('chai').expect
const getFooValue = () => {
return new Promise(resolve => {
setTimeout(() => {
resolve('foo')
}, 500)
})
}
describe('#getFooValue()', function () {
let responseData
before(done => {
getFooValue().then(data => {
responseData = data
done()
})
.catch(err => {
done(err)
})
})
it('response has a value of foo', () => {
expect(responseData).to.equal('foo');
})
it('response is a String', () => {
expect(responseData).to.be.a('String');
})
})
What you're doing now is:
You define the Promise.
You (prematurely) call done and Mocha proceeds to execute the it tests.
The it tests run while responseData is still undefined.
The Promise within before eventually resolves and assigns the responseData variable.
...but at that point it's too late. The tests have already run.
The use of done together with promises is an antipattern because this often results in incorrect control flow, like in this case. All major testing frameworks already support promises, including Mocha.
If default timeout (2 seconds) is not big enough for a promise to resolve, timeout value should be increased, e.g. as explained in this answer by setting it for current test suite (this in describe context). Notice that arrow function should be replaced with regular function to reach suite context.
It should be:
describe('AC 1: This is suite one', function () {
this.timeout(60000);
before(() => {
return promiseResp.then((data) => {
responseData = data;
});
});
it('Test1', () => {
expect(responseData.measure.abc).not.toBe(responseData.measure_list.abc);
});
...
No need for catch for a promise; promise rejections will be handled by the framework. No need for done in tests; they are synchronous.
There's no promise in your it(), so no reason for done(), but it should be called inside then() as it is a callback.
And overall it's cleaner to use async/await. It doesn't work well in before() though.
Also 'function()' is preferable in describe() to set timeout for the tests (Invoking it as a chained method never worked on my experience)
describe('AC 1: This is suite one', function() {
this.timeout(12000); //12 sec timeout for each it()
before((done) => {
promiseResp().then((data) => {
responseData = data;
done();
})
})
it('Test1', () => {
expect(responseData.measure.abc).not.toBe(responseData.measure_list.abc);
});
it('Test2', () => {
expect(responseData.measure.abc).not.toBe(responseData.measure_list.abc);
});
});
I want to keep a log during my integration test suite. I'm testing that every 'item' is being compiled and logging how much time it took. I'm using node 4.3.
First of all I create the log file:
before(function() {
if (!fs.existsSync("./log.csv")) {
fs.writeFile("./log.csv", "Name; Time");
}
});
Then within each it block I would do this:
for (const item of items) {
it('compiles', function() {
return item.testCompile();
});
}
And item class has these methods:
testCompile() {
return this
.buildItem()
.then(result => {
// whatever testing stuff
});
}
buildItem() {
return this
.internalLogicForCompiling()
.then(result => {
// This is not appending anything
fs.appendFile("./log.csv", `${item.name}; ${result.compileTime}`);
return result;
});
}
So far the file is created but never updated... Any clue what I'm doing wrong?
PS: I assume if the file doesn't exists, fs should throw an error, however it doesn't.
Your code is generally ignoring the fact that your fs calls are asynchronous. Promises are not magic. If you use code that is asynchronous but does not use promises, you need to do more than plop that code in a promise can call it done.
The easiest way to deal with the issue would be to use fs.writeFileSync and fs.appendFileSync instead of the calls you make. Otherwise, you should write your before like this:
before(function(done) {
if (!fs.existsSync("./log.csv")) {
fs.writeFile("./log.csv", "Name; Time", done);
}
});
I've just added the done callback.
And buildItem could be something like this so that the promise it returns won't resolve before appendFile is done doing its work:
buildItem() {
return this
.internalLogicForCompiling()
.then(result => {
return new Promise((resolve, reject) => {
fs.appendFile("./log.csv", `${item.name}; ${result.compileTime}`, (err) => {
if (err) {
reject(err);
return;
}
resolve(result);
});
});
});
}
I have the following promises. I know they are working correctly, as the console.log output in funcThree returns the correct data. How do I prove this through testing?
Basically, how do I test this promise? I have tried to tests below, but no matter what I put in there (including expect(false).to.be.true), it always returns true. I do not believe it is actually reaching the expect statements.
CODE
let number = 0;
let count = 0;
// Find Mongoose document
function funcOne(query) {
return new Promise(function (resolve, reject) {
MongooseDocument.find(query)
.exec(function (err, results) {
if (err) {
reject(err);
}
resolve(results);
});
});
}
// Iterate over each document and adjust external value
function funcTwo(documents) {
return new Promise(function (resolve, reject) {
_.each(documents, function (document) {
count += 1;
number += document.otherNumber;
});
if (count >= documents.length) {
resolve({
count,
number,
});
}
});
}
// Chain promises and return result
function funcThree(query) {
return funcOne(query).then(funcTwo).then(function (result) {
console.log("==================");
console.log(result);
return result;
});
}
TEST EXAMPLE
// The expect test below never runs! How do I test this promise? Or
// better yet, how do I get the value returned as a result from the
// promise to test that?
it('should return an object', function () {
funcThree(query).then(function(result) {
return expect(result).to.be.an('object');
});
});
When using chai and chai-as-promised you need to instruct chai to actually use chai-as-promised.
var chai = require('chai');
chai.use(require('chai-as-promised');
Also to convert the assertion to return a promise you need to use the keyword eventually and you need to return the promise back to it(). When using chai-as-promised you need to perform the assertion against the actual promise returning function, in this case, funcThree(query). This is why you're always having your test function return true, you're not actually waiting for the Promise to resolve and since no error sent back to your it() then it is assumed succesful. So your test example above should be as follows:
it('should return an object', function () {
return expect(funcThree(query)).to.eventually.be.a('object');
});
You can also make multiple assertions against the same promise using the following syntax
it('should return an object', function () {
var funcThreePromise = funcThree(query);
// Promise below is whatever A+ Promise library you're using (i.e. bluebird)
return Promise.all([
expect(funcThreePromise).to.eventually.be.fulfilled,
expect(funcThreePromise).to.eventually.be.a('object')
]);
});
The following test is behaving oddly:
it('Should return the exchange rates for btc_ltc', function(done) {
var pair = 'btc_ltc';
shapeshift.getRate(pair)
.then(function(data){
expect(data.pair).to.equal(pair);
expect(data.rate).to.have.length(400);
done();
})
.catch(function(err){
//this should really be `.catch` for a failed request, but
//instead it looks like chai is picking this up when a test fails
done(err);
})
});
How should I properly handle a rejected promise (and test it)?
How should I properly handle a failed test (ie: expect(data.rate).to.have.length(400);?
Here is the implementation I'm testing:
var requestp = require('request-promise');
var shapeshift = module.exports = {};
var url = 'http://shapeshift.io';
shapeshift.getRate = function(pair){
return requestp({
url: url + '/rate/' + pair,
json: true
});
};
The easiest thing to do would be to use the built in promises support Mocha has in recent versions:
it('Should return the exchange rates for btc_ltc', function() { // no done
var pair = 'btc_ltc';
// note the return
return shapeshift.getRate(pair).then(function(data){
expect(data.pair).to.equal(pair);
expect(data.rate).to.have.length(400);
});// no catch, it'll figure it out since the promise is rejected
});
Or with modern Node and async/await:
it('Should return the exchange rates for btc_ltc', async () => { // no done
const pair = 'btc_ltc';
const data = await shapeshift.getRate(pair);
expect(data.pair).to.equal(pair);
expect(data.rate).to.have.length(400);
});
Since this approach is promises end to end it is easier to test and you won't have to think about the strange cases you're thinking about like the odd done() calls everywhere.
This is an advantage Mocha has over other libraries like Jasmine at the moment. You might also want to check Chai As Promised which would make it even easier (no .then) but personally I prefer the clarity and simplicity of the current version
As already pointed out here, the newer versions of Mocha are already Promise-aware. But since the OP asked specifically about Chai, it's only fair to point out the chai-as-promised package which provides a clean syntax for testing promises:
using chai-as-promised
Here's how you can use chai-as-promised to test both resolve and reject cases for a Promise:
var chai = require('chai');
var expect = chai.expect;
var chaiAsPromised = require("chai-as-promised");
chai.use(chaiAsPromised);
...
it('resolves as promised', function() {
return expect(Promise.resolve('woof')).to.eventually.equal('woof');
});
it('rejects as promised', function() {
return expect(Promise.reject('caw')).to.be.rejectedWith('caw');
});
without chai-as-promised
To make it really clear as to what's getting tested, here's the same example coded without chai-as-promised:
it('resolves as promised', function() {
return Promise.resolve("woof")
.then(function(m) { expect(m).to.equal('woof'); })
.catch(function(m) { throw new Error('was not supposed to fail'); })
;
});
it('rejects as promised', function() {
return Promise.reject("caw")
.then(function(m) { throw new Error('was not supposed to succeed'); })
.catch(function(m) { expect(m).to.equal('caw'); })
;
});
Here's my take:
using async/await
not needing extra chai modules
avoiding the catch issue, #TheCrazyProgrammer pointed out above
A delayed promise function, that fails, if given a delay of 0:
const timeoutPromise = (time) => {
return new Promise((resolve, reject) => {
if (time === 0)
reject({ 'message': 'invalid time 0' })
setTimeout(() => resolve('done', time))
})
}
// ↓ ↓ ↓
it('promise selftest', async () => {
// positive test
let r = await timeoutPromise(500)
assert.equal(r, 'done')
// negative test
try {
await timeoutPromise(0)
// a failing assert here is a bad idea, since it would lead into the catch clause…
} catch (err) {
// optional, check for specific error (or error.type, error. message to contain …)
assert.deepEqual(err, { 'message': 'invalid time 0' })
return // this is important
}
assert.isOk(false, 'timeOut must throw')
log('last')
})
Positive test is rather simple. Unexpected failure (simulate by 500→0) will fail the test automatically, as rejected promise escalates.
Negative test uses the try-catch-idea. However: 'complaining' about an undesired pass happens only after the catch clause (that way, it does not end up in the catch() clause, triggering further but misleading errors.
For this strategy to work, one must return the test from the catch clause. If you want't to test anything else, use another it()-block.
Thre is a better solution. Just return the error with done in a catch block.
// ...
it('fail', (done) => {
// any async call that will return a Promise
ajaxJson({})
.then((req) => {
expect(1).to.equal(11); //this will throw a error
done(); //this will resove the test if there is no error
}).catch((e) => {
done(e); //this will catch the thrown error
});
});
this test will fail with following message: AssertionError: expected 1 to equal 11
I'm having trouble with how to properly structure a test for my Promise-returning API with Vows, e.g.
topic:function() { return myfunc() { /* returns a Bluebird Promise */ } },
'this should keep its promise':function(topic) {
var myfunc = topic;
myfunc()
.then(function(result) {
assert(false);
})
.catch(function(error) {
assert(false);
})
.done();
}
My vow never fails. This is my first attempt at using vows to test promises. Hoping someone familiar with this will lend a hand.
In advance, thank you.
Enrique
Since unlike libraries like Mocha - Vows does not yet have support for testing promises, we use its regular asynchronous test format that takes callbacks:
topic:function() { return myfunc() { /* returns a Bluebird Promise */ } },
'this should keep its promise':function(topic) {
var myfunc = topic;
myfunc() // call nodeify to turn a promise to a nodeback, we can chain here
.nodeify(this.callback); // note the this.callback here
}
Here is how it would look with mocha:
describe("Promises", function(){
it("topics", function(){
return myfunc(); // chain here, a rejected promise fails the test.
});
})
The following example is using a when js style promise with vows. You should be able to adapt it to whatever flavor of promise you are using. The key points are:
1) Make sure you call this.callback when your promise resolves. I assign 'this' to a variable in the example below to make sure it is properly available when the promise resolves.
2) Call this.callback (see below how this is done with the variable) with an err object and your result. If you just call it with your result, vows will interpret it as an error.
vows.describe('myTests')
.addBatch({
'myTopic': {
topic: function() {
var vow = this;
simpleWhenPromise()
.then(function (result) {
vow.callback(null, result);
})
.catch(function (err) {
vow.callback(result, null);
});
},
'myTest 1': function(err, result) {
// Test your result
}
},
})