node.jsjestjsgoogle-cloud-pubsubeventemitter

Testing eventEmitter which has an async function listener using jest


I defined an async function which has await statements in it and using this as a listener to on message of pubsub subscription. Would like to test this using jest. But facing a problem with it.

code:


const pubsubPull = () => {
  const pubsub = new PubSub();
  const subscription = pubsub.subscription("test-sub");
  const handler = async message => {
    try {
      const payload = Buffer.from(message.data, "base64").toString();
      const data = JSON.parse(payload).data;
      await repository.insert(data);
      message.ack();
    } catch (err) {
      console.log("error")
    }
  };
  subscription.on("message", handler);
};

test:

jest.mock("@google-cloud/pubsub");
jest.mock("./repository");

describe("listener", () => {
  const data = {
    id: "3ce91594-f648-41bf-9a37-4fa09c4afb3b",
    name: "ABCD",
    value: 1234
  };
  const eventBuffer = Buffer.from(JSON.stringify(data));
  const message = {
    id: "1561624788277",
    data: eventBuffer,
    ack: jest.fn()
  };

  const mockSubscripton = new EventEmitter();
  const pubsubClient = {
    subscription: jest.fn().mockReturnValue(mockSubscripton)
  };

  beforeAll(() => {
    PubSub.mockImplementation(() => pubsubClient);
    repository.insert.mockResolvedValue(true);
  });

  it("should ack the message", (done) => {
    pubsubPull();
    mockSubscripton.emit("message", message);

    expect(message.ack).toHaveBeenCalled();
    done();
  });
});

Test is failing with Expected mock function to have been called. But it was not called. That means assertion is done even before the completion of listener function execution. How can I wait for it to be done and run the assertion. Any help appreciated. node v10.11.0, jest "24.8.0"


Solution

  • A naive but simple solution would be to use a setTimeout to make your assertions, giving your async subscriber enough time to execute.

    it("should ack the message", done => {
      pubsubPull();
      mockSubscripton.emit("message", message);
    
      setTimeout(() => {
        expect(message.ack).toHaveBeenCalled();
        done();
      }, 100);
    });