Suresh Prajapati
Suresh Prajapati

Reputation: 4477

Correct way to use done() while testing asyc/await with mocha

I am practicing basic unit test cases with mocha and a bit confused HOW and WHEN to use done() handler.

  1. How to use done() ?

Below is my sample code where I am not able to use done:

it('Testing insertDocumentWithIndex', async (done) => {
  try{
    var data = await db.insertDocumentWithIndex('inspections', {
      "inspectorId" : 1,
      "curStatus" : 1,
      "lastUpdatedTS" : 1535222623216,
      "entryTS" : 1535222623216,
      "venueTypeId" : 1,
      "location" : [
        45.5891279,
        -45.0446183
      ]
    })
    expect(data.result.n).to.equal(1);
    expect(data.result.ok).to.equal(1);
  }
  catch(e){
    logger.error(e);
    done(e);
  }
})

When I run, it fails and throws an error-

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

But done should be called in case of failure only(Please correct me If I am missing something, I am a beginner😄), which I have done in catch block and coming to the second point of returning a Promise, well that works fine. See below code

it('Testing insertDocumentWithIndex', async () => {
  return new Promise(async (resolve, reject) => {
    try{
      var data = await db.insertDocumentWithIndex('inspections', {
        "inspectorId" : 1,
        "curStatus" : 1,
        "lastUpdatedTS" : 1535222623216,
        "entryTS" : 1535222623216,
        "venueTypeId" : 1,
        "location" : [
          45.5891279,
          -45.0446183
        ]
      })
      expect(data.result.n).to.equal(1);
      expect(data.result.ok).to.equal(1);
      resolve()
    }
    catch(e){
      reject(e);
    }
  })
});

But this requires an additional Promise construction code which is antipattern. But it raises another question

  1. WHEN done should be used ?

Any help or suggestion for a better approach for writing test cases with mocha will help.

Upvotes: 17

Views: 14240

Answers (2)

Your problem is not caused by where you use done parameter, as @estus-flask told you, done is not used with async/await pattern. Your problem is due the promise is taking more than the default 2000ms timeout for the test function; you have to increase that time. One way is using --timeout 5000 (more or less) in the command line when you call mocha, but the recommended way is to set it into each describe function, or even into each it function like this:

describe('Your description', function() {
  this.timeout(7000);
  // your tests
});

or

describe('Your description', function() {
  it('Your test', async function())
    this.timeout(7000);
    // your tests with await
  });
  it('Another no-async test', function(done) {
    // your not-async test
    done();
  });
});

Remember to not use arrow functions when you use the this keyword:

describe('Your description', () => {
  this.timeout(7000); // It will not work.
  // your tests
});

Upvotes: 0

Estus Flask
Estus Flask

Reputation: 222875

The correct way is to not use done with async..await. Mocha supports promises and is able to chain a promise that is returned from it, etc. functions. And async function is syntactic sugar for a function that always returns a promise:

it('Testing insertDocumentWithIndex', async () => {
    var data = await db.insertDocumentWithIndex('inspections', {
      "inspectorId" : 1,
      "curStatus" : 1,
      "lastUpdatedTS" : 1535222623216,
      "entryTS" : 1535222623216,
      "venueTypeId" : 1,
      "location" : [
        45.5891279,
        -45.0446183
      ]
    })
    expect(data.result.n).to.equal(1);
    expect(data.result.ok).to.equal(1);
})

done is needed only for testing asynchronous APIs than don't involve promises. Even then, converting to promises often results in cleaner control flow.

And this

it('Testing insertDocumentWithIndex', async () => {
  return new Promise(async (resolve, reject) => {
  ...

is promise construction antipattern, which is even worse because of async promise constructor callback.

These concerns also apply to other JS testing frameworks with similar API, Jest and Jasmine.

Upvotes: 17

Related Questions