用玩笑模拟 nodemailer.createTransport.sendMail

Dan*_*ats 8 unit-testing mocking node.js nodemailer jestjs

我有一些使用nodemailer模块的代码。

在路由器(router.js)中,我有

const transporter = nodeMailer.createTransport(emailArgs);
Run Code Online (Sandbox Code Playgroud)

然后在路线(/login)内我有:

...
return transporter.sendMail(mailOptions);
Run Code Online (Sandbox Code Playgroud)

我正在尝试使用jest测试框架测试这条路线。我在模拟对sendMail. 我读了这篇关于如何使用 jest mocking 的好博文,但我收到了这个错误:

类型错误:无法读取未定义的属性“sendMail”

事实上,当我检查transporter它的值是未定义的。

这是我的测试代码(不起作用):

import request from "supertest";
import router from "./router";

jest.mock("nodemailer");

describe("", () => {
...

    test("", async () => {
        // 1 - 200 status code; 2 - check email was sent
        expect.assertions(2);

        const response = await request(router)
            .post("/login")
            // global variable
            .send({ "email": email })
            .set("Accept", "application/json")
            .expect("Content-Type", /json/);

        // should complete successfully
        expect(response.status).toBe(200);
        // TODO not sure how to express the expect statement here
    });
});
Run Code Online (Sandbox Code Playgroud)

所以我的问题是如何模拟模块返回的类实例的方法

小智 10

为了模拟 nodemailer 模块,我这样做

jest.mock('nodemailer', () => ({
  createTransport: jest.fn().mockReturnValue({
    sendMail: jest.fn().mockReturnValue((mailoptions, callback) => {})
  })
}));
Run Code Online (Sandbox Code Playgroud)

奇迹般有效

如果需要评估.toBeCalledWith()等,您还可以定义一个模拟函数:

const sendMailMock = jest.fn()
jest.mock('nodemailer', () => ({
  createTransport: jest.fn().mockImplementation(() => ({
    sendMail: sendMailMock,
  })),
}))
Run Code Online (Sandbox Code Playgroud)

  • 有没有办法从那里进入模拟?(例如,在 sendMail 上使用 toHaveBeenCalled() 吗?) (3认同)

Ben*_*ock 8

我遇到了同样的问题并找到了解决方案。这是我发现的:

jest.mock("nodemailer");你告诉笑话来代替nodemailer与自动模拟。这意味着 的每个属性nodemailer都替换为一个空的模拟函数(类似于jest.fn())。

这就是你得到错误的原因 TypeError: Cannot read property 'sendMail' of undefined。为了有一些有用的东西,你必须定义nodemailer.createTransport.

在我们的例子中,我们不想拥有一个带有属性的对象 sendMail。我们可以用nodemailer.createTransport.mockReturnValue({"sendMail": jest.fn()});. 由于您可能想要测试是否sendMail被调用,因此最好事先创建该模拟函数。

这是您的测试代码的完整示例:

import request from "supertest";
import router from "./router";

const sendMailMock = jest.fn(); // this will return undefined if .sendMail() is called

// In order to return a specific value you can use this instead
// const sendMailMock = jest.fn().mockReturnValue(/* Whatever you would expect as return value */);

jest.mock("nodemailer");

const nodemailer = require("nodemailer"); //doesn't work with import. idk why
nodemailer.createTransport.mockReturnValue({"sendMail": sendMailMock});

beforeEach( () => {
    sendMailMock.mockClear();
    nodemailer.createTransport.mockClear();
});

describe("", () => {
...

    test("", async () => {
        // 1 - 200 status code; 2 - check email was sent
        expect.assertions(2);

        const response = await request(router)
            .post("/login")
            // global variable
            .send({ "email": email })
            .set("Accept", "application/json")
            .expect("Content-Type", /json/);

        // should complete successfully
        expect(response.status).toBe(200);

        // TODO not sure how to express the expect statement here
        expect(sendMailMock).toHaveBeenCalled();
    });
});
Run Code Online (Sandbox Code Playgroud)

  • 对于任何其他人试图让它发挥作用。上面应该有一个模拟返回值,内容为:`nodemailer.createTransport.mockReturnValue({sendMail: sendMailMock});`(不带引号) (4认同)