【问题标题】:mock nodemailer.createTransport.sendMail with jest用玩笑模拟 nodemailer.createTransport.sendMail
【发布时间】: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
    });
});

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

【问题讨论】:

    标签: node.js unit-testing mocking jestjs nodemailer


    【解决方案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】:

    这对我有用

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

    class CreateTransportClass {
      sendMail(){
        //console.log("mocked mailer");
      }
    }
    const createTransport = ()=>{
      return new CreateTransportClass()
    }
    
    
    module.exports = {
      createTransport
    }
    1. 在 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"。确保在测试和主题文件中都这样做。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-01-06
        • 2020-02-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-10-06
        相关资源
        最近更新 更多