Home > OS >  Asserting the behavior of an handler of an EventEmitter event
Asserting the behavior of an handler of an EventEmitter event

Time:11-20

I am new to jest, sorry if this is a trivial question but I went through the official jest docs and I am not able to find a solution to the problem.

I am developing a very simple app in nodejs that consumes data from a websocket and propagates it downstream to a set of consumers via zeromq.

The code is the following:

app.js:

const initializer = require("./dependencyInitializer");

const sock = initializer.zmqSock();
const ws = initializer.wsClient();

ws.on('update', data => {
  sock.send([data.topic, JSON.stringify(data)]);
});

The websocket client is a class from a third party library extending from EventEmitter.

I would like to create a test asserting that the sock.send function is called exactly once inside the handler of the 'update' event.

This is my approach:

app.spec.js:

const ws = require("./app");
const initializer = require("./dependencyInitializer");

jest.mock("./dependencyInitializer", () => {
    return {
        wsClient: jest.fn(() => {
            const EventEmitter = require("events")
            const emitter = new EventEmitter()

            return emitter;
        }),
        zmqSock: jest.fn(() => {
            return {
                send: jest.fn()
            }
        })
    }
});
describe('on message received from websocket',() => {
    it('should pass it to zmq', () => {
        const data = {result: "ok"};

        expect(initializer.wsClient).toHaveBeenCalledTimes(1);
        expect(initializer.zmqSock).toHaveBeenCalledTimes(1);

        const _sock = initializer.zmqSock();
        const _ws = initializer.wsClient();
        _ws.emit("update", data);
        expect(_sock.send).toHaveBeenCalledTimes(1);
    });
});

The test fails with the following:

on message received from websocket › should pass it to zmq

    expect(jest.fn()).toHaveBeenCalledTimes(expected)

    Expected number of calls: 1
    Received number of calls: 0

      28 |         const _ws = initializer.wsClient();
      29 |         _ws.emit("update", data);
    > 30 |         expect(_sock.send).toHaveBeenCalledTimes(1);
         |                            ^
      31 |     });
      32 | });

I am not sure if I am on the right path, I would like to understand what would be the best approach to develop a test like this.

Thanks

CodePudding user response:

After mocking by jest.mock(), when the .wsClient() and .zmqSock() methods are called in the app.js file and app.spec.js file, the sock and ws objects in app.js are different with the app.spec.js.

{
  wsClient: jest.fn(() => {
    const EventEmitter = require("events")
    const emitter = new EventEmitter()
    return emitter;
  })
}

Every time you call .wsClient(), it will create a new object.

Emitter can only listen for events from its own emit. The solution is to create the mock emitter and sock objects in the mock factory.

app.js:

const initializer = require('./dependencyInitializer');

const sock = initializer.zmqSock();
const ws = initializer.wsClient();

ws.on('update', (data) => {
  sock.send([data.topic, JSON.stringify(data)]);
});

module.exports = { sock, ws };

app.test.js:

const app = require('./app');
const initializer = require('./dependencyInitializer');

jest.mock(
  './dependencyInitializer',
  () => {
    const EventEmitter = require('events');
    const emitter = new EventEmitter();
    const mSock = { send: jest.fn() };
    return {
      wsClient: jest.fn(() => emitter),
      zmqSock: jest.fn(() => mSock),
    };
  },
  { virtual: true }
);
describe('on message received from websocket', () => {
  it('should pass it to zmq', () => {
    const data = { result: 'ok' };

    expect(initializer.wsClient).toHaveBeenCalledTimes(1);
    expect(initializer.zmqSock).toHaveBeenCalledTimes(1);

    const _sock = initializer.zmqSock();
    const _ws = initializer.wsClient();

    // check if they have same reference
    expect(app.sock).toBe(_sock);
    expect(app.ws).toBe(_ws);

    _ws.emit('update', data);
    expect(_sock.send).toHaveBeenCalledTimes(1);
  });
});

test result:

 PASS  examples/70024105/app.test.js (9.279 s)
  on message received from websocket
    ✓ should pass it to zmq (2 ms)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |     100 |      100 |     100 |     100 |                   
 app.js   |     100 |      100 |     100 |     100 |                   
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        10.112 s
  • Related