I am attempting to mock a class Mailer
using jest and I can\'t figure out how to do it. The docs don\'t give many examples of how this works. The process is the
Just for Googlers and future visitors, here's how I've setup jest mocking for ES6 classes. I also have a working example at github, with babel-jest for transpiling the ES module syntax so that jest can mock them properly.
__mocks__/MockedClass.js
const stub = {
someMethod: jest.fn(),
someAttribute: true
}
module.exports = () => stub;
Your code can call this with new, and in your tests you can call the function and overwrite any default implementation.
example.spec.js
const mockedClass = require("path/to/MockedClass")();
const AnotherClass = require("path/to/AnotherClass");
let anotherClass;
jest.mock("path/to/MockedClass");
describe("AnotherClass", () => {
beforeEach(() => {
mockedClass.someMethod.mockImplementation(() => {
return { "foo": "bar" };
});
anotherClass = new AnotherClass();
});
describe("on init", () => {
beforeEach(() => {
anotherClass.init();
});
it("uses a mock", () => {
expect(mockedClass.someMethod.toHaveBeenCalled();
expect(anotherClass.settings)
.toEqual(expect.objectContaining({ "foo": "bar" }));
});
});
});
You don't have to mock your mailer class but the mailgun-js
module. So mailgun is a function that returns the function messages
that return the function send
. So the mock will look like this.
for the happy path
const happyPath = () => ({
messages: () => ({
send: (args, callback) => callback()
})
})
for the error case
const errorCase = () => ({
messages: () => ({
send: (args, callback) => callback('someError')
})
})
as you have this 2 cases it make sense to mock the module inside your test. First you have to mock it with a simple spy where we later can set the implementation for our cases and then we have to import the module.
jest.mock('mailgun-js', jest.fn())
import mailgun from 'mailgun-js'
import Mailer from '../../../../server/services/emails/mailer'
As your module uses promises we have 2 options either return the promise from the test or use async/await
. I use the later one for more info have a look here.
test('test the happy path', async() => {
//mock the mailgun so it returns our happy path mock
mailgun.mockImplementation(() => happyPath)
//we need to use async/awit here to let jest recognize the promise
const send = await Mailer.send();
expect(send).toBe('The email was sent successfully!')
});
If you would like to test that the mailgun send
method was called with the correct parameter you need to adapt the mock like this:
const send = jest.fn((args, callback) => callback())
const happyPath = () => ({
messages: () => ({
send: send
})
})
Now you could check that the first parameter for send was correct:
expect(send.mock.calls[0][0]).toMatchSnapshot()