Wednesday, 11 September 2019

NodeJS: How to assert if event callback function was called using sinon

How can I test if a callback function from a event listener is called? For example, I have the following code where app.js initializes the application through the init.js controller.

The main.js file has a class which extends and Event Emitter, making the object an event emitter.

app.js

const initController = require('./init');
async function init() {
    initController.startMain();
}
init();

main.js

const events = require('events'),
ui = require('./ui');

module.exports.getMain = function () {
  class Main extends events.EventEmitter {
    constructor() {
      super();
      this.status = null;
    }
  }
  return new Main();
};

module.exports.init = () => {
  const main = this.getMain();
  ui.init(main);
  this.start(main);
}

module.exports.start = (main) => {
  ui.start(main);
  main.emit('http-init');
  main.emit('http-success');
  main.emit('http-error');
};

ui.js

function init(main) {
    main.on('http-init', onHttpInit.bind(this));
    main.on('http-success', onHttpSuccess.bind(this));
    main.on('http-error', onHttpError.bind(this));
    main.once('app-ready', onAppReady.bind(this));
  };

  function start (main) {};

  function onAppReady() {
    console.log('APP READY');
  };

  function onHttpInit() {
    console.log('HTTP INIT SEQUENCE');
  };

  function onHttpError(error) {
    console.log('HTTP ERROR SEQUENCE');
  };

  function onHttpSuccess() {
      console.log('HTTP SUCCESS SEQUENCE');
  };

  module.exports = exports = {
    init,
    start,
    onHttpInit,
    onHttpError,
    onHttpSuccess,
  };

init.js

exports.startMain = () => {

    console.log('Start application');

    // Load application modules
    const main = require('./main');

    // Start the application
    main.init();
 };

So, when I run the command node app.js, I see the following output

Start application HTTP INIT SEQUENCE HTTP SUCCESS SEQUENCE HTTP ERROR SEQUENCE

which means that the listeners are active and that the functions are called.

ui.tests.js

const sinon = require('sinon'),
    main = require('../main').getMain(),
    proxyquire = require('proxyquire').noPreserveCache().noCallThru();

describe('UI Tests', () => {
    const sandbox = sinon.createSandbox();
    let controller = null;
    before(() => {
        controller = proxyquire('../ui', {});
    })
    describe('Testing Eventlisteners', ()=> {
        afterEach(() => {
            main.removeAllListeners();
        });
        const eventMap = new Map([
        [ 'http-init', 'onHttpInit' ],
        [ 'http-success', 'onHttpSuccess' ],
        [ 'http-error', 'onHttpError']
        ]);
        eventMap.forEach((value, key) => {
            it(`should register an eventlistener on '${key}' to ${value}`, () => {
                controller.init(main);
                const stub = sinon.stub(controller, value);
                main.emit(key);
                sinon.assert.called(stub);
            })
        })
    })
})    

However, when I run the above test, even though I get the output, i.e. the functions were called, however, sinon assert always fails saying the below:

  UI Tests
    Testing Eventlisteners
HTTP INIT SEQUENCE
      1) should register an eventlistener on 'http-init' to onHttpInit
HTTP SUCCESS SEQUENCE
      2) should register an eventlistener on 'http-success' to onHttpSuccess
HTTP ERROR SEQUENCE
      3) should register an eventlistener on 'http-error' to onHttpError


  0 passing (16ms)
  3 failing

 1) UI Tests
       Testing Eventlisteners
         should register an eventlistener on 'http-init' to onHttpInit:
     AssertError: expected onHttpInit to have been called at least once but was never called
      at Object.fail (node_modules/sinon/lib/sinon/assert.js:106:21)
      at failAssertion (node_modules/sinon/lib/sinon/assert.js:65:16)
      at Object.assert.(anonymous function) [as called] (node_modules/sinon/lib/sinon/assert.js:91:13)
      at Context.it (test/ui.tests.js:25:30)
  2) UI Tests
       Testing Eventlisteners
         should register an eventlistener on 'http-success' to onHttpSuccess:
     AssertError: expected onHttpSuccess to have been called at least once but was never called
      at Object.fail (node_modules/sinon/lib/sinon/assert.js:106:21)
      at failAssertion (node_modules/sinon/lib/sinon/assert.js:65:16)
      at Object.assert.(anonymous function) [as called] (node_modules/sinon/lib/sinon/assert.js:91:13)
      at Context.it (test/ui.tests.js:25:30)

  3) UI Tests
       Testing Eventlisteners
         should register an eventlistener on 'http-error' to onHttpError:
     AssertError: expected onHttpError to have been called at least once but was never called
      at Object.fail (node_modules/sinon/lib/sinon/assert.js:106:21)
      at failAssertion (node_modules/sinon/lib/sinon/assert.js:65:16)
      at Object.assert.(anonymous function) [as called] (node_modules/sinon/lib/sinon/assert.js:91:13)
      at Context.it (test/ui.tests.js:25:30)


I do not know why the tests fail even though the function was called at least once, which is seen by the outputs HTTP INIT SEQUENCE, HTTP SUCCESS SEQUENCE and HTTP ERROR SEQUENCE when I run the tests.

I tried doing stub.should.have.been.called;. With this the tests pass, however, it's not really passing the tests as both stub.should.have.been.called; or stub.should.not.have.been.called; pass the test regardless, instead of the latter failing the test.

Anybody know the reason for this failing test? Thank you for any help.



from NodeJS: How to assert if event callback function was called using sinon

No comments:

Post a Comment