3

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.

AshanPerera
  • 596
  • 1
  • 7
  • 18
  • I found a problem if your code, why do you call both resolve() and reject() in function dbStatusSuccess()? – iKoala Nov 24 '16 at 08:55
  • That was a mistake. I removed that already. But I don't see how that should affect how a promise works? The issue is the timeout error, which I get even if I use the done() function passed from it() or if I return the promise – AshanPerera Nov 24 '16 at 08:57
  • It will give expected result because a promise cannot be resolved and rejected at the same time. – iKoala Nov 24 '16 at 09:02
  • Am I missing something here? If the default timeout is 2000ms then obviously your example times out or doesn't it? Isn't the solution just increasing the timeout limit? – fahrradflucht Nov 24 '16 at 09:05
  • okay I'm removing that bit from the question then, just to make it clear. The issue is with the promises taking 2 seconds to complete/fail. If it completes in 1 second the tests pass. – AshanPerera Nov 24 '16 at 09:06
  • @fahrradflucht This is what I want to test. How can I test a promise which might take longer than 2 seconds to complete? – AshanPerera Nov 24 '16 at 09:09
  • it('should', () => {}).timeout(3000); //this tell mocha to wait for 3000ms – iKoala Nov 24 '16 at 09:18

4 Answers4

4

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.

fahrradflucht
  • 1,563
  • 3
  • 14
  • 22
  • If you want to use arrow function, this.timeout() will not work because arrow function doesn't have this binding. http://stackoverflow.com/a/35398816/3484580 – iKoala Nov 24 '16 at 09:20
  • @iKoala Yes I know, this is why I recommended switching to a plain old function. If you wan't to stick to arrow functions (which wasn't a requirement in the question) you could use the solution you edited into your answer. – fahrradflucht Nov 24 '16 at 09:23
  • Well this works, but then whats the point of returning the promise or calling done() ? I thought by doing either of those we can make mocha wait for the promise to be fulfilled or rejected? – AshanPerera Nov 24 '16 at 09:31
  • 1
    @Ashan77 The point is that otherwise mocha would just move on and won't wait for the expectations that you make in your `.catch` or `.then` blocks. But you would still wan't to timeout if your promises take forever to resolve. – fahrradflucht Nov 24 '16 at 09:35
  • Thanks a lot! I spent way too much time thinking about this. The documentation wasn't very clear on this. – AshanPerera Nov 24 '16 at 09:37
0
  '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);
    });
  });
  1. 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
  2. Don't use arrow function in descibe and it. More info
galkin
  • 5,264
  • 3
  • 34
  • 51
  • Still the same error `Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.` – AshanPerera Nov 24 '16 at 08:58
  • 1
    I think there should not be done callback in function that is passed to describe(). – teroi Nov 24 '16 at 08:59
-1

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
 });
iKoala
  • 870
  • 4
  • 11
-1

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

Mark Williams
  • 2,268
  • 1
  • 11
  • 14
  • 1
    This is not right, Mocha supports Promise so you don't need to callback done when using Promise. – iKoala Nov 24 '16 at 08:51
  • OK, yes I see it supports promises, but according to the Mocha docs (at the link I posted) you have to return the promise whereas the code in the post was asserting (expect) in the resolution of the promise. – Mark Williams Nov 24 '16 at 09:06
  • Yes, that's right you always have to place your assertion code inside the resolution block. I just said you shouldn't use done when returning a promise to mocha#it. It will actually gives you error when you want to use done when returning a promise to mocha#it. – iKoala Nov 24 '16 at 09:16