用 Jest 模拟 nodemailer

Mocking nodemailer with Jest

我设置了一个允许用户重置密码的端点。一切正常,测试通过,直到我添加了 nodemailer 并包含一行向用户发送电子邮件。

我正在使用 Jest 进行测试。

如果我注释掉发送电子邮件的行,则测试通过,mailer.sendPasswordResetEmail(body.email, token);如果我保留该行 - 我的测试失败。我已经与 REST 客户端确认一切正常,这让我相信测试是问题所在。

ReferenceError:您正在尝试在 Jest 环境被拆除后导入文件。

test("if a valid user requests a password change, they should be assigned a hash", async () => {
    const userBefore = await helper.getUser();

    expect(userBefore.passwordResetHash).toBe("");

    await api
      .post("/api/reset-password")
      .send({ email: userBefore.email })
      .expect(200);

    const userAfter = await helper.getUser();

    expect(userAfter.passwordResetHash).not.toBeNull();
  });

我认为我没有正确地模拟 nodemailer - 有没有人有将 nodemailer 和 jest 一起使用的经验?或者有更好的方法吗

有问题的文件是 controllers/resetPassword.jsutils/mailer.jstests/resetPassword.test.js

controllers/resetPassword.js

resetPasswordRouter.post("/", async (request, response) => {
  // get email from request
  const { body } = request;

  // get the user with matching email
  const user = await User.findOne({ email: body.email });

  // if not found return error
  if (!user) {
    return response.status(400).json({ error: "User not found" });
  }

  // if user generate a token
  const token = helper.generateToken();

  // create a new user object with the resetPasswordHash defined
  const update = {
    passwordResetHash: await bcrypt.hash(token, 10),
  };

  // update user model with the password hash
  const updatedUser = await User.findByIdAndUpdate(user.id, update, {
    new: true,
  });

  mailer.sendPasswordResetEmail(body.email, token);

  // setup timer to reset password hash in 30 minutes
  setTimeout(async () => {
    await User.findByIdAndUpdate(
      user.id,
      { passwordResetHash: "" },
      { new: true }
    );
  }, 30000); // half hour

  // return the updated user with the hash set
  response.status(200).json(updatedUser);
});

utils/mailer.js

const nodemailer = require("nodemailer");
const config = require("../utils/config");

const mailer = nodemailer.createTransport({
  host: "smtp.mailtrap.io",
  port: 1111,
  auth: {
    user: "8b4f30425e75ea",
    pass: "8b4f30425e75ea",
  },
});

const sendPasswordResetEmail = (email, token) => {
  const sitename = config.SITENAME;
  const resetPasswordLink = `${sitename}/api/reset-password/verify?email=${email}&token=${token}`;

  mailer.sendMail({
    to: email,
    from: config.FROM_EMAIL,
    subject: `Password Reset | ${sitename}`,
    html: `<h1>Password Reset</h1>
           <p>Hello, you\'ve requested a password reset.</p>
           <p><a href="${resetPasswordLink}">Click here to reset your password</a>, if you did not make this request please disregard the email.</p>`,
  });
};

module.exports = {
  sendPasswordResetEmail,
};

您可以在此处找到存储库:https://github.com/gerrgg/gregpress


ReferenceError: You are trying to import a file after the Jest environment has been torn down.

您需要模拟函数 mailer.sendPasswordResetEmail 因此当控制器调用它时,它实际上会调用您的模拟实现。

test("if a valid user requests a password change, they should be assigned a hash", async () => {
    const userBefore = await helper.getUser();

    expect(userBefore.passwordResetHash).toBe("");

    // Jest spy to intercept mailer.sendPasswordResetEmail call
    let spy = jest.spyOn(mailer, 'sendPasswordResetEmail').mockImplementation(() => return true);

    await api
      .post("/api/reset-password")
      .send({ email: userBefore.email })
      .expect(200);

    const userAfter = await helper.getUser();

    expect(userAfter.passwordResetHash).not.toBeNull();
});