How to mock es6 class using Jest

匿名 (未验证) 提交于 2019-12-03 01:32:01

问题:

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 I will have a node event password-reset that is fired and when that event is fired, I want to send an email using Mailer.send(to, subject, body). Here is my directory structure:

project_root -- __test__ ---- server ------ services -------- emails ---------- mailer.test.js -- server ---- services ------ emails -------- mailer.js -------- __mocks__ ---------- mailer.js 

Here is my mock file __mocks__/mailer.js:

const Mailer = jest.genMockFromModule('Mailer');  function send(to, subject, body) {   return { to, subject, body }; }  module.exports = Mailer; 

and my mailer.test.js

const EventEmitter = require('events'); const Mailer = jest.mock('../../../../server/services/emails/mailer');  test('sends an email when the password-reset event is fired', () => {   const send = Mailer.send();   const event = new EventEmitter();   event.emit('password-reset');   expect(send).toHaveBeenCalled(); }); 

and finally my mailer.js class:

class Mailer {    constructor() {     this.mailgun = require('mailgun-js')({       apiKey: process.env.MAILGUN_API_KEY,       domain: process.env.MAILGUN_DOMAIN,     });   }    send(to, subject, body) {     return new Promise((reject, resolve) => {       this.mailgun.messages().send({         from: 'Securely App ',         to,         subject: subject,         html: body,       }, (error, body) => {         if (error) {           return reject(error);         }          return resolve('The email was sent successfully!');       });     });   }  }  module.exports = new Mailer(); 

So, how do I successfully mock and test this class, using Jest? Many thanks for helping!

回答1:

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() 


回答2:

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" }));     });   });  }); 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!