237

In my node application I'm using mocha to test my code. While calling many asynchronous functions using mocha, I'm getting timeout error (Error: timeout of 2000ms exceeded.). How can I resolve this?

var module = require('../lib/myModule');
var should = require('chai').should();

describe('Testing Module', function() {

    it('Save Data', function(done) {

        this.timeout(15000);

        var data = {
            a: 'aa',
            b: 'bb'
        };

        module.save(data, function(err, res) {
            should.not.exist(err);
            done();
        });

    });


    it('Get Data By Id', function(done) {

        var id = "28ca9";

        module.get(id, function(err, res) {

            console.log(res);
            should.not.exist(err);
            done();
        });

    });

});
Talha Awan
  • 4,573
  • 4
  • 25
  • 40
sachin
  • 13,605
  • 14
  • 42
  • 55
  • is it an integration test? it's a lot of time for a test to run - maybe you should consider stubs - https://github.com/thlorenz/proxyquire might help you. – surui May 17 '13 at 11:10
  • May I recommend using promises for asynchronous stuff and testing it then is a breeze with [Chai as promise](https://www.npmjs.com/package/chai-as-promised) – Krym Feb 19 '15 at 12:53

7 Answers7

381

You can either set the timeout when running your test:

mocha --timeout 15000

Or you can set the timeout for each suite or each test programmatically:

describe('...', function(){
  this.timeout(15000);

  it('...', function(done){
    this.timeout(15000);
    setTimeout(done, 15000);
  });
});

For more info see the docs.

Aruna Herath
  • 6,241
  • 1
  • 40
  • 59
Andreas Hultgren
  • 14,763
  • 4
  • 44
  • 48
  • 4
    shorter version is ```-t```. if you use mocha-test to run mocha from grunt task, this is also supported in options object ```options:{timeout:15000}```. – svassr Aug 21 '14 at 15:12
  • For the `it` you must pass in the `done` like `setTimeout(done, 200);` as per the [docs](http://mochajs.org/#suite-specific-timeouts) – Matt Canty Mar 01 '15 at 17:59
  • what if you're using co-wrap around your mocha tests to allow generator functions, then this timeout doesn't work – PositiveGuy Oct 19 '15 at 06:34
  • this works for me.. you can set timeout for independent suites that are running async – Gaurav Sharma Apr 30 '17 at 11:31
  • 7
    FYI: passing arrow functions to Mocha is discouraged. http://mochajs.org/#arrow-functions – c0ming May 09 '17 at 03:12
  • 4
    Arrow functions are not discouraged in the link above. It just says you just need to know what they do so you don't screw up when needing to access the context. I never need the context, as relying on timeouts are fragile, and all my tests run in a few ms, but I do come across the same issue when using sinon-test. Still use lambdas 99% of the time. – oligofren Jun 12 '17 at 22:01
  • 34
    `TypeError: this.timeout is not a function` when using `"mocha": "^3.5.0"` – Junior Mayhé Sep 11 '17 at 00:49
  • yes getting the same error as @JuniorM highlighted. Also calling done with async/await doesn't work. so not helpful answer for our case – Adil Jan 31 '18 at 09:04
  • 6
    @adi are you sure you are not using arrow functions? Regarding async/await it's in the docs so should work (and is the same thing as using promises). Sounds like another question though. – Andreas Hultgren Feb 04 '18 at 19:54
  • @JuniorM You can increase your test timeout by updating scripts in your package.json. See Daniel Mbeyah answer below. – RayLoveless Mar 13 '19 at 17:57
  • Please update the answer to use the form "it(...test).timeout(newTimeout)" since this is the one that works now. Updating the timeout globally seems like a bad idea, since it can hide things. I would think you want to do it in a very targeted way. – Vectorjohn Jan 28 '20 at 17:01
88

I find that the "solution" of just increasing the timeouts obscures what's really going on here, which is either

  1. Your code and/or network calls are way too slow (should be sub 100 ms for a good user experience)
  2. The assertions (tests) are failing and something is swallowing the errors before Mocha is able to act on them.

You usually encounter #2 when Mocha doesn't receive assertion errors from a callback. This is caused by some other code swallowing the exception further up the stack. The right way of dealing with this is to fix the code and not swallow the error.

When external code swallows your errors

In case it's a library function that you are unable to modify, you need to catch the assertion error and pass it onto Mocha yourself. You do this by wrapping your assertion callback in a try/catch block and pass any exceptions to the done handler.

it('should not fail', function (done) { // Pass reference here!

  i_swallow_errors(function (err, result) {
    try { // boilerplate to be able to get the assert failures
      assert.ok(true);
      assert.equal(result, 'bar');
      done();
    } catch (error) {
      done(error);
    }
  });
});

This boilerplate can of course be extracted into some utility function to make the test a little more pleasing to the eye:

it('should not fail', function (done) { // Pass reference here!
    i_swallow_errors(handleError(done, function (err, result) {
        assert.equal(result, 'bar');
    }));
});

// reusable boilerplate to be able to get the assert failures
function handleError(done, fn) {
    try { 
        fn();
        done();
    } catch (error) {
        done(error);
    }
}

Speeding up network tests

Other than that I suggest you pick up the advice on starting to use test stubs for network calls to make tests pass without having to rely on a functioning network. Using Mocha, Chai and Sinon the tests might look something like this

describe('api tests normally involving network calls', function() {

    beforeEach: function () {
        this.xhr = sinon.useFakeXMLHttpRequest();
        var requests = this.requests = [];

        this.xhr.onCreate = function (xhr) {
            requests.push(xhr);
        };
    },

    afterEach: function () {
        this.xhr.restore();
    }


    it("should fetch comments from server", function () {
        var callback = sinon.spy();
        myLib.getCommentsFor("/some/article", callback);
        assertEquals(1, this.requests.length);

        this.requests[0].respond(200, { "Content-Type": "application/json" },
                                 '[{ "id": 12, "comment": "Hey there" }]');
        expect(callback.calledWith([{ id: 12, comment: "Hey there" }])).to.be.true;
    });

});

See Sinon's nise docs for more info.

oligofren
  • 20,744
  • 16
  • 93
  • 180
  • I have a huge suite of tests and I just went through all the promises in my specs to make sure they're all calling `done()` at the end of the promise and I'm already mocking the network calls using Angular's `$httpBackend`, but no luck. Wrapping every single spec with a try-catch doesn't seem very pragmatic. Any other suggestions? thanks! – Gustavo Matias Dec 26 '15 at 02:24
  • @GustavoMatias You actually haven't mentioned what your problem is, just stated that this is not a solution to whatever you are having problems with. Please do elaborate :-) Are your tests not failing fast enough? Are they failing at times, but you would like to know why? Hard to guess what you intend to achieve. – oligofren Jan 03 '16 at 03:54
  • hi @oligofren! that wasn't the best explanation indeed. There's a more detailed explanation of my problem here http://stackoverflow.com/questions/34510048/angular-mocha-memory-leak?noredirect=1#comment56767589_34510048 thanks! – Gustavo Matias Jan 03 '16 at 20:45
  • "In general, the cleanest way (but ugliest) way of dealing with this problem is to wrap your code with a try/catch and pass any exceptions to the done handler." No, this is not at all the cleanest way. Not by a long shot. The cleanest way is to write code that does not swallow exceptions. Every time I've seen someone complain that Mocha was not detecting a failed test, that was because there was something swallowing the exception. Adding a `try.... catch...` works *around* the bug in the code under test rather than *fix* it. – Louis Jul 19 '16 at 16:31
  • @Louis you might possibly be right about the whys here, but I am unable to verify it out of the blue. anyway, people have a problem with Mocha _seemingly_ being unable to catch some error, and this is a way of handling it. your given approach assumes that the code swallowing the error is not some library function or similar, in which case it would not be so easily solved. – oligofren Jul 25 '16 at 22:28
  • @Louis I finally got around to updating the answer. Of course, you are right, and it was basically poor wording. I have now made the wording more explicit, to say that this behavior is caused by the code you are passing the callback into, not Mocha, and that this is the code that should be fixed. The utility function is just there for the cases where it is unfeasible to fix the underlying code (for some reason). – oligofren Oct 31 '17 at 13:13
  • For integration tests, where things shouldn't be mocked (that's what functional tests do), I'm assuming the only alternative to "speeding up the network" is to increase the timeout. – Ellesedil Dec 10 '18 at 23:52
  • That's right. Increasing timeouts is about your only option. – oligofren Dec 11 '18 at 08:06
29

If you are using arrow functions:

it('should do something', async () => {
  // do your testing
}).timeout(15000)
lukas_o
  • 3,776
  • 4
  • 34
  • 50
8

A little late but someone can use this in future...You can increase your test timeout by updating scripts in your package.json with the following:

"scripts": { "test": "test --timeout 10000" //Adjust to a value you need }

Run your tests using the command test

Daniel Mbeyah
  • 297
  • 3
  • 5
2

For me the problem was actually the describe function, which when provided an arrow function, causes mocha to miss the timeout, and behave not consistently. (Using ES6)

since no promise was rejected I was getting this error all the time for different tests that were failing inside the describe block

so this how it looks when not working properly:

describe('test', () => { 
 assert(...)
})

and this works using the anonymous function

describe('test', function() { 
 assert(...)
})

Hope it helps someone, my configuration for the above: (nodejs: 8.4.0, npm: 5.3.0, mocha: 3.3.0)

syberkitten
  • 234
  • 4
  • 9
0

My issue was not sending the response back, so it was hanging. If you are using express make sure that res.send(data), res.json(data) or whatever the api method you wanna use is executed for the route you are testing.

il0v3d0g
  • 655
  • 6
  • 14
0

Make sure to resolve/reject the promises used in the test cases, be it spies or stubs make sure they resolve/reject.

kavigun
  • 2,219
  • 2
  • 14
  • 33