用玩笑模拟 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