Hyra
Hyra

Reputation: 828

Using Jest spyOn a module that is initiated

Within an ExpresS API I'm using the Postmark library to send an email, which is initiated like this:

var postmark = require("postmark");
var client = new postmark.Client("aaaa-bbbbb-cccc");

And then used to send a password reset mail later on with:

client.sendEmailWithTemplate(
    // Options here
);

Now, I would like to test this function has been called, but I have difficulties finding out how to mock/spy on this.

I have tried the following (simplified):

const request = require("supertest");
const app = require("../server/app");

const postmark = require("postmark");
jest.mock("postmark");

describe("API Tests", () => {
    test("it should give a reset link when requesting with existing e-mail address", () => {
        return request(app)
        .post("/api/auth/passwordreset")
        .send({
            email: "[email protected]"
        })
        .then(response => {
            expect(postmark.Client).toHaveBeenCalled();
        });
    });

});

This works, but it's only testing if postmark has been used, since I can't figure out how to actually test the client.sendEmailWithTemplate method

Any suggestions on how to accomplish this?

EDIT: following up on @samanime answer I created a repo to illustrate the 'challenge'

https://github.com/Hyra/jest_test_example

Upvotes: 1

Views: 1896

Answers (1)

samanime
samanime

Reputation: 26547

You can specifically mock out the Client function that is returned by the mocked postmark to return an object with mocked functions.

In Jest, you can provide specific mocking code for a node_modules by creating a folder named __mocks__ at the same level as node_modules, i.e.,

/project-root
  /node_modules
  /__mocks__

Note, that is two underscores on each side.

In there, make a function named <package_name>.js (in your case, postmark.js). It will then load whatever is exported by that when you use the mock.

In that file, you can mock it out as needed. Something like this would probably work:

// global jest
module.exports = {
  Client: jest.fn(() => ({
      sendEmailWithTemplate: jest.fn(() => {})
  }))
};

It doesn't have to be as compact as this, but basically it makes postmark have a function called Client which returns an object that has a functiono called sendEmailWithTemplate, both of which are mocks/spys.

Then you can just check if postmark.Client.sendEmailWithTemplate was called.

The one gotcha is you'll need to be sure to reset all of these in between tests. You could do this manually in your beforeEach(), but if you are going to reuse it, I like to add an extra function named __reset() which will reset the code and just call that:

// global jest
const mockedPostmark = {
  Client: jest.fn(() => ({
      sendEmailWithTemplate: jest.fn(() => {})
  }))
};

mockedPostmark.__reset = () => {
  mockedPostmark.Client.mockClear();
  mockedPostmark.Client.sendEmailWithTemplate.mockClear();
};

module.exports = mockedPostmark;

You can add additional functions as needed as well.

Upvotes: 1

Related Questions