用玩笑模拟 nodemailer.createTransport.sendMail

Posted

技术标签:

【中文标题】用玩笑模拟 nodemailer.createTransport.sendMail【英文标题】:mock nodemailer.createTransport.sendMail with jest 【发布时间】:2019-04-24 12:34:34 【问题描述】:

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

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

const transporter = nodeMailer.createTransport(emailArgs);

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

...
return transporter.sendMail(mailOptions);

我正在尝试使用jest 测试框架测试这条路线。我在模拟对sendMail 的调用时遇到了一些麻烦。我阅读了 this nice blogpost 关于如何使用 jest mocking 的信息,但我收到了这个错误:

TypeError: 无法读取未定义的属性“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
    );
);

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

【问题讨论】:

【参考方案1】:

模拟 nodemailer 模块我做

jest.mock('nodemailer', () => (
  createTransport: jest.fn().mockReturnValue(
    sendMail: jest.fn().mockReturnValue((mailoptions, callback) => )
  )
));

像魅力一样工作

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

const sendMailMock = jest.fn()
jest.mock('nodemailer', () => 
  createTransport: jest.fn().mockReturnValue(
    sendMail: sendMailMock
  )

【讨论】:

有没有办法从那里去模拟? (例如,在该 sendMail 上使用 toHaveBeenCalled() ?)【参考方案2】:

这对我有用

    mocks/nodemailer.js 目录下创建一个 mock 文件(参见 Jest Manual Mock for reference) 将以下代码添加到文件中。 createTransport 方法需要返回一个包含 sendMail 方法才能工作的响应。所以请看下面使用的代码

class CreateTransportClass 
  sendMail()
    //console.log("mocked mailer");
  

const createTransport = ()=>
  return new CreateTransportClass()



module.exports = 
  createTransport
    在 jest 配置文件 (jest.config.js) 中将文件路径添加到 testPathIgnorePatterns,如下所示:



    testPathIgnorePatterns: ["/__mocks__/nodemailer.js"],

这应该可以完美运行。

【讨论】:

【参考方案3】:

好吧,我仍然希望我的邮件程序能够工作并且返回 undefined 不起作用,所以我不得不将 sendMailMock 更改为:

const sendMailMock = jest.fn((mailOptions, callback) => callback());

【讨论】:

【参考方案4】:

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

使用 jest.mock("nodemailer"); 你告诉 jest 用自动模拟替换 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();
    );
);

【讨论】:

对于其他试图让它工作的人。上面应该有一个 mockReturnValue 读取:nodemailer.createTransport.mockReturnValue(sendMail: sendMailMock);(不带引号) 要让它与 ES6 模块一起工作,您需要使用 import * as nodemailer from "nodemailer"。确保在测试和主题文件中都这样做。

以上是关于用玩笑模拟 nodemailer.createTransport.sendMail的主要内容,如果未能解决你的问题,请参考以下文章

用玩笑模拟 nodemailer.createTransport.sendMail

用玩笑模拟自定义事件发射器

不能用玩笑模拟模块,并测试函数调用

尝试用玩笑制作一个简单的 Axios 模拟

用玩笑进行单元测试时,如何以角度模拟 ResizeObserver polyfill?

用玩笑模拟material-ui选择组件