For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves

I have this test of nodejs when testing I get a error of done function not declared.

Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.

My test code is, I have the done call back but still getting the error to call the done();

    it('remove existing subdocument', (done) => {
    const Vic = new User({
      name: 'Vic',
      posts: [{ title: 'Leaning Nodejs' }]
    });

    vic.save()
      .then(() => User.findOne({ name: 'Vic' }))
      .then((user) => {
        const post = user.posts[0];
        post.remove();
        return user.save();
      })
      .then(() => User.findOne({ name: 'Vic' }))
      .then((user) => {
        assert(user.posts.length === 0);
        done();
      });
  });

Solution 1:

I know an ugly way of doing it, just by increasing default timeout of Mocha from 2 seconds to 10 seconds this can be done by adding a flag --timeout 10000 in the test scripts i.e -

package.json

 "scripts": {
    "start": "SET NODE_ENV=dev && node server.js",
    "test": "mocha --timeout 10000"
  }

Solution 2:

I was facing the same issue, @MFAL's link in comment helped. I am expanding upon it.

When there is an error/incorrect assertion an error is raised inside the promise. This leads to promise rejection. Once rejected done is never called and mocha reports time out. I solved this by writing a .catch block and chaining it with the promise:

          it('resolves', (done) => {
            fooAsyncPromise(arg1, arg2).then((res, body) => {
                expect(res.statusCode).equal(incorrectValue);
                done();
            }).catch(done);
         });

Other ways as mentioned in the Wietse's blog are:

To chain a then(done, done) which handles both resolve and reject of the promise.

         it('resolves', (done) => {
           resolvingPromise.then( (result) => {
             expect(result).to.equal('promise resolved');
           }).then(done, done);
         });

Return a promise:

        it('resolves', () => {
          return resolvingPromise.then( (result) => {
            expect(result).to.equal('promise resolved');
          });
        });

Use async/wait:

        it('assertion success', async () => {
          const result = await resolvingPromise;
          expect(result).to.equal('promise resolved'); 
        });

Solution 3:

You can just add timeout to the specific test to increase/override the default timeout which is 2 seconds. I had the same issue but was able to by pass it using:

it('Test', (done) => { 
//your code  
done();
}).timeout(10000);

Solution 4:

My problem was the timeout itself (extending it didn't help) so my solution was

it("it should test methods",function(){
     this.timeout(0);
   });

as you see you don't need the done argument