Javascript 如何使用 Jest 模拟 es6 类

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/43749845/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 02:08:40  来源:igfitidea点击:

How to mock es6 class using Jest

javascriptunit-testingjestjs

提问by dericcain

I am attempting to mock a class Mailerusing 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-resetthat 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:

我正在尝试Mailer使用玩笑来模拟一个课程,但我不知道该怎么做。文档没有给出很多例子来说明这是如何工作的。该过程是我将password-reset触发一个节点事件,当该事件被触发时,我想使用Mailer.send(to, subject, body). 这是我的目录结构:

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:

这是我的模拟文件__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

和我的 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.jsclass:

最后是我的mailer.js课:

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 <[email protected]>',
        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!

那么,我如何使用 Jest 成功地模拟和测试这个类?非常感谢您的帮助!

采纳答案by Andreas K?berle

You don't have to mock your mailer class but the mailgun-jsmodule. So mailgun is a function that returns the function messagesthat return the function send. So the mock will look like this.

您不必模拟邮件程序类,而是模拟mailgun-js模块。所以mailgun是一个返回函数的函数messages,返回函数send。所以模拟看起来像这样。

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.

由于您有这两种情况,因此在测试中模拟模块是有意义的。首先,您必须使用一个简单的 spy 来模拟它,然后我们可以为我们的案例设置实现,然后我们必须导入模块。

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.

当您的模块使用 promise 时,我们有 2 个选项可以从测试中返回 promise 或使用async/await. 我使用后一个了解更多信息,请看这里

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 sendmethod was called with the correct parameter you need to adapt the mock like this:

如果您想测试是否send使用正确的参数调用了 mailgun方法,您需要像这样调整模拟:

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

回答by Justus Romijn

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.

仅针对 Google 员工和未来的访问者,以下是我为 ES6 类设置玩笑模拟的方法。我在 github 上也有一个工作示例,使用 babel-jest 来转换 ES 模块语法,以便 jest 可以正确模拟它们。

__mocks__/MockedClass.js

__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.

您的代码可以使用 new 调用它,并且在您的测试中您可以调用该函数并覆盖任何默认实现。

example.spec.js

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

});