Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jest: how to mock a dependency that listens to events?

I came across a very complicated situation. I'll try to keep it as concise as possible.

So I have a code like this in myModule.js:

const lib = require('@third-party/lib');

const myFunction = () => {
  const client = lib.createClient('foo');
  return new Promise((resolve, reject) => {
    client.on('error', (err) => reject(err));
    client.on('success', () => {
      client.as(param1).post(param2, param3, (err, data) => {
        if (err) reject(err);

        // Some important logical processing of data
        resolve(data);
      });
    });
  });
}

module.exports = { myFunction };

There are a few things I am able to mock, like: createClient. What I am not able to mock is the event part I don't even know how to do this. And the .as().post() part.

Here's how my jest test looks like:

const myModule = require('./myModule');
const mockData = require('./mockData');

describe('myFunction', () => {

  it('Should resolve promise when lib calls success event', async () => {
    try {
      const myData = await myModule.myFunction();
      expect(myData).toMatchObject(mockData.resolvedData);
    } catch (err) {
      expect(err).toBeNull();
    }
  })
});

Any help, much Appreciated.

I tried to find similar questions but at this point, my mind has just stopped working... Please let me know if you need any more details.

like image 703
Vijay Gaikwad Avatar asked Jan 21 '19 11:01

Vijay Gaikwad


1 Answers

Here’s what you need to do:

// EventEmitter is here to rescue you
const events = require("events");

// Mock the third party library
const lib = require("@third-party/lib");

lib.createClient.mockImplementationOnce(params => {
  const self = new events.EventEmitter();

  self.as = jest.fn().mockImplementation(() => {
    // Since we're calling post on the same object.
    return self;
  });

  self.post = jest.fn().mockImplementation((arg1, _cb) => {
    // Can have a conditional check for arg 1 if so desird
    _cb(null, { data : "foo" });
  });

  // Finally call the required event with delay.
  // Don't know if the delay is necessary or not.
  setInterval(() => {
    self.emit("success");
  }, 10);
  return self;
}).mockImplementationOnce(params => {
  const self = new events.EventEmitter();

  // Can also simulate event based error like so:
  setInterval(() => {
    self.emit("error", {message: "something went wrong."});
  }, 10);
  return self;
}).mockImplementationOnce(params => {
  const self = new events.EventEmitter();
  self.as = jest.fn().mockImplementation(() => {
    return self;
  });

  self.post = jest.fn().mockImplementation((arg1, _cb) => {
    // for negative callback in post I did:
    _cb({mesage: "Something went wrong"}, null);
  });

  setInterval(() => {
    self.emit("success");
  }, 10);
  return self;
});

This is only the mock object that you need to put in your test.js file.

Not sure if this code will work as is, although won’t require a lot of debugging.

If you just want to positive scenario, remove the second mockImplementationOnce and replace the first mockImplementationOnce with just mockImplementation.

like image 186
darth-coder Avatar answered Nov 15 '22 04:11

darth-coder