Flame_Phoenix
Flame_Phoenix

Reputation: 17584

Test async function with mocha and sinon

Background

I am made a small function that emits messages via sockets and I am trying to test it using mocha and sinon:

const myFun = socket => {

    socket.emit("first", "hello World!");

    //some random amount of time passes
    socket.emit("second", "hello Moon!");

    //other random amount of time passes
    socket.emit("third", "hello Mars? Venus? I dunno...");
};

Using sinon I can pass to my function a fakeSocket:

const fakeSocket = {
    emit: sinon.spy()
};

And check if I emit or not the messages.

Problem

The problem here is that I don't know when my test ends. Because myFun does not return a promise and I don't have a final message I don't know how to tell mocha that I have sent all the messages I wanted and that the test should end.

Test

const chai = require("chai");
const expect = chai.expect;
const chaiAsPromised = require("chai-as-promised");
chai.use(chaiAsPromised);
const sinon = require("sinon");
const sinonChai = require("sinon-chai");
chai.use(sinonChai);

describe("myFun", () => {

    const fakeSocket = {
            emit: sinon.spy()
        };

    it("receive first message", done => {

        myFun(fakeSocket);

        setTimeout(() => {
            try{
                expect(fakeSocket.emit).to.have.been.calledThrice;
                done();
            }catch(err){
                done(err);
            }
        }, 1000);
        //1 seconds should be more than enough to let the test complete.
    });

});

I am using a setTimeout with a try catch to test the code, which honestly is quite horrible.

Question

Upvotes: 0

Views: 1208

Answers (1)

robertklep
robertklep

Reputation: 203484

If you know that a particular message is always set last, you can instruct Sinon to call a function when that message is emitted. That function can be Mocha's callback function for asynchronous tests:

describe("myFun", function() {
  // Extend timeout and "slow test" values.
  this.timeout(3000).slow(10000);

  const fakeSocket = { emit: sinon.stub() }

  it('should emit a certain final message', done => {
    fakeSocket.emit.withArgs('third', 'hello Mars? Venus? I dunno...')
                   .callsFake(() => done());
    myFun(fakeSocket);
  });
});

So if socket.emit() is called with arguments 'third', 'hello Mars?...', Sinon will call a function that calls the done callback, signalling to Mocha that the test has completed.

When, for some reason, that particular message isn't emitted, done never gets called and the test will time out (after 3s), which is an indication that either the test has failed, or it took more time than expected.

If you don't know what the first argument to socket.emit() is going to be for the last message, only the message itself, the test becomes this:

it('should emit a certain final message', done => {
  fakeSocket.emit.callsFake((name, msg) => {
    if (msg === 'hello Mars? Venus? I dunno...') {
      done();
    }
  });
  myFun(fakeSocket);
})

Upvotes: 1

Related Questions