Paradoxical issue with mocha done() and async await

Viewed 3844

I have the following test case:

it("should pass the test", async function (done) {
        await asyncFunction();
        true.should.eq(true);
        done();
    });

Running it asserts:

Error: Resolution method is overspecified. Specify a callback or return a Promise; not both.

And if I remove the done(); statement, it asserts:

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

How to solve this paradox?

3 Answers

Removing done as a param from it worked for me! Instead only use expect/should. Example is as follows:

getResponse(unitData, function callBack(unit, error, data){ try {
    return request.post(unit, function (err, resp) {
        if (!err && resp.statusCode === 200) {
            if (resp.body.error) {
                return callback(obj, JSON.stringify(resp.body.error), null); 
            }
            return callback(obj, null, resp); 
        } else {
            if (err == null) {  
                err = { statusCode: resp.statusCode, error: 'Error occured.' };
            }
            return callback(obj, err, null); 
        }
    });
} catch (err) {
    return callback(obj, err, null);
}}

BEFORE:

it('receives successful response', async (done) => { 
const getSomeData = await getResponse(unitData, function callBack(unit, error, data){ 
    expect(data.statusCode).to.be.equal(200); 
    done(); 
}) })

AFTER (works):

it('receives successful response', async () => { 
const getSomeData = await getResponse(unitData, function callBack(unit, error, data){
     expect(data.statusCode).to.be.equal(200); 
}) })

Sometimes there are cases you need to use async/await + done function in mocha.

For example, in one of my socket.io unit test cases, I have to call db functions with async functions and test socket event handlers which are callback functions:

context("on INIT_CHAT", ()=> {
  it("should create a room", async (done) => {
    const user = await factory.create("User");
    socket.emit("INIT_CHAT", user);
    
    socket.on("JOIN_CHAT", async (roomId) => {
       const room = await ChatRoom.findByPk(roomId);
       expect(room).to.exist;
       // then I need to close a test case here
       done();
    });
  });
});

This will causes the exact same error as in the OP:

Error: Resolution method is overspecified. Specify a callback or return a Promise; not both.

My Workaround:

I just wrapped the entire test code in a promise generator:

context("on INIT_CHAT", ()=> {
  it("should create a room", async () => {
    const asyncWrapper = () => {
      return new Promise(async (resolve) => {
        const user = await factory.create("User");
        socket.emit("INIT_CHAT", user);
        socket.on("JOIN_CHAT", async (roomId) => {
          const room = await ChatRoom.findByPk(roomId);
          expect(room).to.exist;
          resolve(true);
        });
      });
    });
    await asyncWrapper();
  });
});
Related