57

After the upgrade, Mocha can not even run a simple test here is the code

const assert = require('assert');

it('should complete this test', function (done) {
  return new Promise(function (resolve) {
    assert.ok(true);
    resolve();
   })
  .then(done);
});

I took this code from here

I understood that it now throws an exception Error: Resolution method is overspecified. Specify a callback * or * return a Promise; not both.

But how to make it work? I did not understand. I have

node -v 6.9.4

mocha -v 3.2.0

How to run this code are now in a new and correct format?

Louis
  • 146,715
  • 28
  • 274
  • 320
coder fire
  • 993
  • 2
  • 11
  • 24

7 Answers7

62

Just drop
.then(done); and replace function(done) with function()

You are returning a Promise so calling done is redundant as it said in error message

In the elder versions you had to use callback in case of async methods like that

it ('returns async', function(done) {
   callAsync()
   .then(function(result) {
      assert.ok(result);
      done();
   });
})

Now you have an alternative of returning a Promise

it ('returns async', function() {
  return new Promise(function (resolve) {
     callAsync()
       .then(function(result) {
          assert.ok(result);
          resolve();
       });
  });
})

But using both is misleading (see for example here https://github.com/mochajs/mocha/issues/2407)

Igor Popov
  • 2,084
  • 16
  • 18
20

Mocha allows to either use a callback:

it('should complete this test', function (done) {
  new Promise(function (resolve) {
    assert.ok(true);
    resolve();
   })
  .then(done);
});

OR return a promise:

it('should complete this test', function () {
  return new Promise(function (resolve) {
    assert.ok(true);
    resolve();
   });
});

// Or in the async manner
it('should complete this test', async () => {
    await Promise.resolve();
    assert.ok(true);
});

You can't do both.

Simon Boudrias
  • 42,953
  • 16
  • 99
  • 134
  • 1
    Another alternative: you can change `function ()` to `async function ()` and `return` to `await`. Is that accurate? Thanks. – goodvibration May 08 '18 at 09:17
9

I had to removed the done from the function parameter and the done() of the function call Before

   before(async function (done) {
        user = new User({ ...});
        await user.save();
        done()
    });

After

   before(async function () {
        user = new User({ ...});
        await user.save();
    });

These works for me

kheengz
  • 840
  • 12
  • 10
3

I had this same issue. A lot of times Mocha is paired with another library called Chai. Chai has a package called "chai-as-promised". It gives you the super simple ability to write less code and test promises. In your case of just testing if a promise resolves, it seems perfect.

const chai = require('chai');
const chaiAsPromised = require("chai-as-promised");
const should = require("chai").should();
chai.use(chaiAsPromised);

describe("Testing with correct syntax and non repeated names", () => {
    it("Should give us a positive response", () => {
      graphQL.sendToGQL(model,"specialEndpoint").should.eventually.be.an("Object");
    })
})

Justin Rice
  • 1,111
  • 12
  • 13
3

An example of async functions with done breaking.

Failure Case

it('If the credentials exists in the system it should return the token generated against it.', async (done) => {
    let aObj = await admin.createAdmin();
    chai.request(server)
    .post("/authenticate")
    .set("Content-Type", "application/x-www-form-urlencoded")
    .send({username: aObj.login,password:aObj.password})
    .end((err, res) => {
        res.should.have.status(200);
        res.body.should.be.a("string");
        done();
    });
});

Success Case

it('If the credentials exists in the system it should return the token generated against it.', async () => {
    let adminObj = await admin.createAdmin();
    chai.request(server)
    .post("/auth/login")
    .set("Content-Type", "application/x-www-form-urlencoded")
    .send({username: adminObj.login,password:adminObj.password})
    .end((err, res) => {
        res.should.have.status(200);
        res.body.should.be.a("string");
        // done();
    });
});
Haisum Usman
  • 518
  • 5
  • 13
1

If you don't have callbacks, prior answers (which suggest deleting the done) is correct.

If need to both await some external promise, and then exercise a callback/errback-based implementation in your test, that solution doesn't help you.

You can use a library like pify to convert the callback API to use promises.

Alternatively, you can use a Latch in your callback:

  it("test", async () => {
    const l = new Latch()
    const v = await promiseValue()
    s.methodThatTakesCallback((err, result) => {
      expect(result).to.eql(expected)
      l.resolve() // < notifies mocha your test is done
    })
    return l.promise
  })

In TypeScript, here's a very stripped-down Latch implementation:

/**
 * Simple one-count concurrent barrier
 */
export class Latch {
  readonly promise: Promise<void>
  resolve!: () => void
  constructor() {
    this.promise = new Promise<void>(resolve => (this.resolve = resolve))
  }
}
mrm
  • 5,001
  • 2
  • 32
  • 30
1

Just emit done callback completely and use async instead. (This implementation is based on an express api running on firebase functions, using a custom jsonwebtoken)

const { FIREBASE_UID } = require('dotenv').config()?.parsed
const chai = require('chai');
const chaiHttp = require('chai-http');
const server = require('../lib/api').API;
const should = chai.should();
const expect = chai.expect

chai.use(chaiHttp)

const test = chai.request(server).keepOpen()

// get your token with an earlier mock request and store to a var
describe('Just checking a token', () => {

 let some_token
 it('should print custom jwt for testing, status: 200'), async () => {
    try {
        const res = await test.get(`/createCustomFirebaseToken/${FIREBASE_UID}`).send()
        res.should.exist
        res.should.have.status(200);
        res.should.have.json
        some_token = (JSON.parse(res.text)).token
    } catch (error) {
        throw error
    }
}
 it('should print details:PING, status:200'), async () => {
    try {
        const res = await test.get('/').set('Authorization',`Bearer ${some_token}`)
                              .send()
        res.should.exist
        res.should.have.status(200);
        res.should.have.json
        const { details, status } = JSON.parse(res.text)
        expect(details).to.equal('PING')
        expect(status).to.equal(200)
    } catch (error) {
        throw error
    }
 }
 after(() => test.close())
})