Testing Promises with Mocha - javascript

I've been trying to test the following code using Mocha, but I always get the error.
Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test
The code I want to test is as follows.
'use strict'
const Promise = require('bluebird');
const successResponse = {status: 'OK'};
const failResponse = {status: 'FAIL'};
function dbStatusSuccess () {
return new Promise(function(resolve, reject) {
setTimeout(() => {
resolve(successResponse);
}, 2010);
});
}
function dbStatusFail () {
return new Promise(function(resolve, reject) {
setTimeout(() => {
reject(failResponse);
}, 2000);
});
}
module.exports = {
dbStatusSuccess,
dbStatusFail
}
and here are my tests.
'use strict'
const Promise = require('bluebird');
const chai = require('chai')
chai.use(require('chai-string'))
chai.use(require('chai-as-promised'));
const expect = chai.expect;
chai.should();
const healthyCheck = require('./healthyCheck');
const resp = {status:'OK'};
const resp2 ={status: 'FAIL'};
describe('healthy-check end point', () => {
it('should return successful response when connected to database', () => {
return healthyCheck.dbStatusSuccess()
.then((res) => {
console.log(JSON.stringify(res, undefined, 2));
return expect(res).to.equal(resp);
}).catch( (err) => {
console.log(err);
return expect(err).to.deep.equal(resp2);
});
});
});
I also get an error { AssertionError: expected { status: 'OK' } to equal { status: 'OK' } in the console. Which I believe is from loggin the err from the .catch function.
EDIT 1.
Removed the reject function from dbStatusSuccess function.
The issue is with the promises taking 2 seconds to complete/fail. If the time set in setTimeout is less than 2 seconds, the tests will pass.

The default timeout in your test seems to be 2000ms. Your code obviously takes longer to complete. Therefore, you have to up the timeout limit. As noted here you shouldn't use arrow functions so you can safely access this.
Then you can increase your timeout like so:
'use strict'
const Promise = require('bluebird');
const chai = require('chai')
chai.use(require('chai-string'))
chai.use(require('chai-as-promised'));
const expect = chai.expect;
chai.should();
const healthyCheck = require('./healthyCheck');
const resp = {status:'OK'};
const resp2 ={status: 'FAIL'};
describe('healthy-check end point', () => {
it('should return successful response when connected to database', function() {
this.timeout(3000);
return healthyCheck.dbStatusSuccess()
.then((res) => {
console.log(JSON.stringify(res, undefined, 2));
return expect(res).to.equal(resp);
}).catch( (err) => {
console.log(err);
return expect(err).to.deep.equal(resp2);
});
});
});
Then your test should run as expected.

'use strict'
const Promise = require('bluebird');
const chai = require('chai');
chai.use(require('chai-string'));
chai.use(require('chai-as-promised'));
const expect = chai.expect;
chai.should();
const healthyCheck = require('./healthyCheck');
describe('healthy-check end point', function() {
it('should return successful response when connected to database', function(done) {
const resp = {status: 'OK'};
healthyCheck.dbStatusSuccess()
.then((res) => {
console.log(JSON.stringify(res, undefined, 2));
expect(res).to.equal(resp);
done();
}).catch(done);
});
});
In code example I don't return promise, so I need use callback. Usage callback in your async tests helps avoid Error: timeout of 2000ms exceeded
Don't use arrow function in descibe and it. More info

You should use the done callback, for example:
it('reads some file', function(done) {
fs.readFile('someFile.json', function(err, data) {
if (err) return done(err);
assert(data != null, "File should exist.");
done();
});
});
What's happening is the test ('it' function) returns before your promise resolves; using done means the test won't finish until you call done() when the promises resolves.
See
http://tobyho.com/2015/12/16/mocha-with-promises/
and
https://mochajs.org/#working-with-promises

Well, I just found the problem, your test is tricky.
You set the timeout timer to 2010ms but Mocha default execution time is 2000ms, so you will always get the error from Mocha.
I still think you shouldn't create .catch block in the returned promise chain, it will stop the promise chain to propagate.
describe('healthy-check end point', () => {
it('should return successful response when connected to database', () => {
return healthyCheck.dbStatusSuccess()
.then((res) => {
console.log(JSON.stringify(res, undefined, 2));
return expect(res).to.equal(resp);
});
}).timeout(2500); //tell Mocha to wait for 2500ms
});

Related

Node: how to test multiple events generated by an async process

I need to test an async Node.js library which periodically generates events (through EventEmitter) until done. Specifically I need to test data object passed to these events.
The following is an example using mocha + chai:
require('mocha');
const { expect } = require('chai');
const { AsyncLib } = require('async-lib');
describe('Test suite', () => {
const onDataHandler = (data) => {
expect(data.foo).to.exist;
expect(data.bar).to.exist;
expect(data.bar.length).to.be.greaterThan(0);
};
it('test 1', async () => {
const asyncLib = new AsyncLib();
asyncLib.on('event', onDataHandler); // This handler should be called/tested multiple times
await asyncLib.start(); // Will generate several 'events' until done
await asyncLib.close();
});
});
The problem is that even in case of an AssertionError, mocha marks the test as passed and the program terminates with exit code 0 (instead of 1 as I expected).
The following uses done callback instead of async syntax, but the result is the same:
require('mocha');
const { expect } = require('chai');
const { AsyncLib } = require('async-lib');
describe('Test suite', () => {
const onDataHandler = (data) => {
expect(data.foo).to.exist;
expect(data.bar).to.exist;
expect(data.bar.length).to.be.greaterThan(0);
};
it('test 1', (done) => {
const asyncLib = new AsyncLib();
asyncLib.on('event', onDataHandler);
asyncLib.start()
.then(asyncLib.close)
.then(() => done());
});
});
I have also tried with a "pure" Node.js approach using the native assert.ok without any 3rd part library:
const { strict: assert } = require('assert');
const { AsyncLib } = require('async-lib');
const test = async () => {
const onDataHandler = (data) => {
assert.ok(data.foo != null);
assert.ok(data.bar != null);
assert.ok(data.bar.length > 0);
};
asyncLib.on('event', onDataHandler);
const asyncLib = new AsyncLib();
await asyncLib.start();
await asyncLib.close();
}
(async () => {
await test();
})();
Even in this case, an AssertionError would make the program to terminate with exit code 0 instead of 1.
How can I properly test this code and make the tests correctly fail in case of an assertion error?
There are some things that you need to fix to make it works:
Make your test async, because the test is going to execute the expects after a certain event is received meaning it's going to be asyncronous.
Your event handler in this case onDataHandler should receive the done callback because there is the way how you can indicate to mocha that the test was finished successful as long as the expects don't fail.
I wrote some code and tested it out and it works, you have to make some changes to adapt your async library though:
describe('Test suite', function () {
const onDataHandler = (data, done) => {
expect(data.foo).to.exist;
expect(data.bar).to.exist;
expect(data.bar.length).to.be.greaterThan(0);
done();
};
it('test 1', async function (done) {
eventEmitter.on('event', (data) => onDataHandler(data, done));
setTimeout(() =>{
eventEmitter.emit('event', {
})
}, 400)
});
});

Promises returns an undefined value

I'm trying to make a test with jest and promises.
When I try to write in console the result get it from the promise I've got an undefined value
This is my testing code:
describe("Search concurrent with promises", () => {
describe("Test 10: Several searches in DDBB", () => {
function connection(){
let ddbb = new PGMappingDDBB();
return new Promise((resolve, reject) => {
let db = ddbb.connect();
//resolve("Hello World");
resolve(db);
});
}
test("Test 12: Several searches concurrently", () => {
connection().then(ddbb => {
console.log(ddbb);
});
});
});
});
ddbb.connect() is an asynchronous function. The code of connect() is:
async connect(){
this.client = await poolMapping.connect();
}
When I try to write the state of the variable ddbb I've got that is undefined.
However, If I comment resolve(db) and remove the comment of resolve("Hello World"), when I write the value of ddbb I've got "Hello World".
What am I doing wrong?
Edit I:
ddbb.connect() returns a Promise. If I write what returns console.log(ddbb.connect()). I've got:
Promise { <pending> }
To explain:
async connect(){
this.client = await poolMapping.connect();
// NOTE: There is no return value
}
describe("Search concurrent with promises", () => {
describe("Test 10: Several searches in DDBB", () => {
function connection(){
let ddbb = new PGMappingDDBB();
return new Promise((resolve, reject) => {
let db = ddbb.connect();
// ^^ this will be undefined, as connect has no return value
resolve(db);
// ^^^^^^^^^^^^ Resolving the promise with undefined
});
}
test("Test 12: Several searches concurrently", () => {
connection().then(ddbb => {
console.log(ddbb);
/// ^^^^ will be undefined
});
});
});
});
Additionally, jest is not waiting for the outcome of the promise, and you are not asserting anything in the test, so it will always succeed.
When dealing with promises, if you return the promise from the test, Jest will wait for it. But you still need to assert on the value.

module.exports = Promise.all not working inside tests

I want to just make sure that my server is running before I create PR's locally to test that nothing is broken.
worker.js
My server exports a promise
module.exports = Promise.all([
hbsPartialsLoaded,
])
.then(() => {
let server;
if (!isTestEnvironment) {
server = app.listen(config.port);
}
return { server, app };
});
tests
let { expect, request } = require('chai')
.use(require('chai-http'))
.use(require('chai-cheerio'));
const config = require('../../app/config');
const worker = require('../../app/worker');
describe('server response', function () {
worker.then(() => {
before(function () {
server.listen(config.port);
});
it('should return 200', function (done) {
request.get(`http://localhost:${config.port}`, function (err, res, body){
expect(res.statusCode).to.equal(200);
done();
});
});
after(function () {
worker.close();
});
})
});
Running Tests
NODE_ENV=test mocha --timeout 20000 --recursive test/ --compilers js:babel-core/register
0 passing (1ms)
My tests are never run. How can I get it so I wait for sever.js to finish its promises before the tests are run?
You didn't export the promise correctly, so the calling is bad, because you may not get callback if the require("worker.js") finished before calling .then, it should be worker().then not worker.then, but that's not the only problem.
The worker.js should return a function that launches the Promise.all. So change the module.exports = Promise.all... to module.exports = () => Promise.all...
Use before to launch the server and call it synchronously. Inside it you can use the asynchronous functions as you want.
The server.listen has a callback on second argument, that tells you when it finishes, so you can use promise and resolve it there.
An example:
var expect = require('chai').expect;
describe('x', () => {
before(() => new Promise((resolve, reject) => setTimeout(resolve, 500)));
it('should work', (done) => setTimeout(() => expect(0).to.be.above(0), 500));
});
Better example for your case:
var expect = require('chai').expect;
var app = require('express')();
describe('x', () => {
before(() => new Promise((resolve, reject) => {
app.listen(3000, () => {
console.log('The server launched');
resolve();
});
}));
it('should work', (done) => {
console.log('I got called after the server got launched! :)');
setTimeout(() => expect(1).to.be.above(0), 500)
});
});
it() functions should be called directly inside describe(). Nested inside a promise doesn't read well nor follow the standard testing process
let { expect, request } = require('chai')
.use(require('chai-http'))
.use(require('chai-cheerio'));
const config = require('../../app/config');
const worker = require('../../app/worker');
describe('server response', function () {
before(function () {
server.listen(config.port);
});
it('should return 200', function (done) {
worker.then(() => {
request.get(`http://localhost:${config.port}`, function (err, res, body){
expect(res.statusCode).to.equal(200);
done();
});
})
});
after(function () {
worker.close();
});
});

Testing js promises with Mocha, Chai, chaiAsPromised and Sinon

I'm trying to test a function that reads in a file and returns a promise with the file's contents.
function fileContents(){
return new Promise(function(resolve, reject) {
fs.readFile(filename, function(err, data){
if (err) { reject(err); }
else { resolve(data); }
});
});
}
The unit test for the above
describe('Testing fileContents', function () {
afterEach(function () {
fs.readFile.restore();
});
it('should return the contents of the fallBack file', function () {
let fileContents = '<div class="some-class">some text</div>';
sinon.stub(fs, 'readFile').returns(function(path, callback) {
callback(null, fileContents);
});
let fileContentsPromise = fileContents();
return fileContentsPromise
.then(data => {
expect(data).to.eventually.equal(fileContents);
});
});
The above test errs with
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
I also tried
describe('Testing fileContents', function () {
afterEach(function () {
fs.readFile.restore();
});
it('should return the contents of the fallBack file', function (done) {
let fileContents = '<div class="some-class">some text</div>';
sinon.stub(fs, 'readFile').returns(function(path, callback) {
callback(null, fileContents);
});
let fileContentsPromise = fileContents();
fileContentsPromise.then(function(data){
expect(data).to.equal(fileContents);
done();
});
});
and got the same error. The function is working in my local site, but I don't know how to write a test for it. I'm new to js. What am I missing?
There are multiple problems with your code. For instance, you redeclare fileContents in your test and assign it a string value, which of course won't work with doing fileContents() in the same test. I'm going to concentrate on two conceptual problems rather than the "duh"-type mistakes like this one.
The two conceptual problems are:
To have fs.readFile call your callback with fake values you must use .yields. Using .returns changes the return value, which you do not use. So stub it like this:
sinon.stub(fs, 'readFile').yields(null, fakeContents);
You are using the .eventually functionality provided by chai-as-promised on a non-promise, but you have to use it on a promise for it to work properly so your test should be:
return expect(fileContentsPromise).to.eventually.equal(fakeContents);
Here's code that works:
const sinon = require("sinon");
const fs = require("fs");
const chai = require("chai");
const chaiAsPromised = require("chai-as-promised");
chai.use(chaiAsPromised);
const expect = chai.expect;
// We need to have filename defined somewhere...
const filename = "foo";
function fileContents(){
return new Promise(function(resolve, reject) {
fs.readFile(filename, function(err, data){
if (err) { reject(err); }
else { resolve(data); }
});
});
}
describe('Testing fileContents', function () {
afterEach(function () {
fs.readFile.restore();
});
it('should return the contents of the fallBack file', function () {
let fakeContents = '<div class="some-class">some text</div>';
sinon.stub(fs, 'readFile').yields(null, fakeContents);
let fileContentsPromise = fileContents();
return expect(fileContentsPromise).to.eventually.equal(fakeContents);
});
});

How to properly stub a promise object which is part of another promise

I have a promise function which performs an authentication based on the clients cookie
const getInitialState = (id_token) => {
let initialState;
return new Promise((resolve,reject) => {
if(id_token == null){
initialState = {userDetails:{username: 'Anonymous',isAuthenticated: false}}
resolve(initialState)
}else{
var decoded = jwt.verify(JSON.parse(id_token),'rush2112')
db.one('SELECT * FROM account WHERE account_id = $1',decoded.account_id)
.then(function(result){
console.log('result is : ',result)
initialState = {userDetails:{username:result.username,isAuthenticated:true}}
resolve(initialState)
})
.catch(function(err){
console.log('There was something wrong with the token',e)
reject('There was an error parsing the token')
})
}
})
}
getInitialState is a promise object which calls a database function(another promise object) if the cookie is valid.
I want to stub the db call here to resolve to a username. But its not working no matter what i try
i tried two libraries sinonStubPromise and sinon-as-promised. But both seem to result in a timeout error which tells me that the db function isnt getting resolved
I believe i'm not stubbing the db function properly
these are the various ways i've tried
stub2 = sinon.stub(db,'one')
stub2.returnsPromise().resolves({username:'Kannaj'})
or
sinon.stub(db,'one').returns({username:'Kannaj'})
or
sinon.stub(db,'one')
.withArgs('SELECT * FROM account WHERE account_id = $1',1)
.returns({username:'Kannnaj'})
or
let db = sinon.stub(db).withArgs('SELECT * FROM account WHERE account_id = $1',1).returns({username:'Kannnaj'})
all lead to a timeout error from mocha
Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.
this is my entire test function
it('should return a valid user if id_token is valid',function(){
id_token = '{"account_id":1}'
console.log('stub1: ',stub1(), typeof(stub1))
console.log('stub2 : ',stub2,typeof(stub2))
// my attempts here
return expect(getInitialState(id_token)).to.eventually.be.true
})
For some reason , i believe mocha/sinon is loosing the pg-promise context as soon as it calls db.any . not sure why.
I can't speak to sinon-as-promised or sinonStubPromise, but you don't need them to accomplish something like this.
const sinon = require('sinon');
const chai = require('chai');
chai.use(require('chai-as-promised'));
const expect = chai.expect;
// real object
const db = {
one: function () {
// dummy function
}
};
// real function under test
function foo () {
return db.one('SELECT * FROM account WHERE account_id = $1');
}
describe('foo', function () {
beforeEach(function () {
sinon.stub(db, 'one')
.withArgs('SELECT * FROM account WHERE account_id = $1')
.returns(Promise.resolve({username: 'Kannaj'}));
});
it('should not timeout', function () {
return expect(foo())
.to
.eventually
.eql({username: 'Kannaj'});
});
afterEach(function () {
db.one.restore();
});
});

Categories

Resources