Sinon 如何对非导出的私有功能进行单元测试?

Sinon how to unit test a non exported private functionality?

我真的是单元测试的新手,我正在尝试使用 sinon.js 模拟 aws.ses 方法,但我担心的是我如何调用 sesConstructor 方法。由于它没有从 ses.js 导出,我在测试套件中声明了一个 sesConstructor 方法。

如果有人能告诉我这是否是一个完整的反模式以及是否有任何其他更好的解决方案(不使用 'rewire' 模块),我将不胜感激

ses.js

let ses = {};

const sesConstructor = () => {
  AWS.config.update({
    accessKeyId: configurations.AWS_CONFIG.ACCESSKEYID,
    secretAccessKey: configurations.AWS_CONFIG.SECRECTACCESSKEY,
    region: configurations.AWS_CONFIG.REGION
  });
  ses = new AWS.SES({ apiVersion: "2010-12-01" });
};

export const sendTemplatedEmail = (emailTo, templateName, templateData) => {
  sesConstructor();

  const params = {
    Destination: {
      ToAddresses: emailTo
    },
    Source: process.env.emailSource,
    Template: templateName,
    TemplateData: JSON.stringify(templateData)
  };

  return ses.sendTemplatedEmail(params).promise();
};

export default { sendTemplatedEmail };

ses.test.js

describe("SES", () => {
  const emailTo = ["test@gmail.com"];
  const templateName = "template";
  const templateData = "test";
  process.env.emailSource = "test@gamil.com";
  const sendEmail = sendTemplatedEmail(emailTo, templateName, templateData);

  it("should return a promise", () => {
    expect(sendEmail).to.be.a("promise");
  });

  describe("sesConstructor", () => {
    it("should call AWS SES", () => {
      const sesConstructor = data => {
        const ses = new AWS.SES(data); // eslint-disable-line no-unused-vars
      };

      const mockAWS = sinon.mock(AWS);

      mockAWS
        .expects("SES")
        .once()
        .withArgs({
          apiVersion: "2010-12-01"
        })
        .returns(true);

      sesConstructor({ apiVersion: "2010-12-01" });

      mockAWS.verify();
      mockAWS.restore();
    });

   
  });
});

实际上,您的 sendTemplatedEmail 函数还做了一件名称中未指明的事情:初始化传输。而且它是 sendTemplatedEmail 的硬依赖。您需要处理此依赖关系以使其更易于测试,可能的解决方案:

1) 将传输实例作为参数传递。考虑下一个:

const sesConstructor = () => {
  //...
  return AWS.SES(...);
};

export const sendTemplatedEmail = (emailTo, templateName, templateData, ses = sesConstructor()) => {
  const params = {...}
  return ses.send(...);
}

在这种情况下,您将新参数添加到 sendTemplatedEmail ses 实例,默认情况下从 sesConstructor() 获得 - 这允许您在测试中传递存根,并轻松测试它。

你也可以把实例放在closure,如果适合你的话:

export const sendTemplatedEmail = (ses = sesConstructor()) => (emailTo, templateName, templateData) => {
  const params = {...}
  return ses.send(...)
}

然后你将使用它:

sendTemplatedEmail()('some@person.com'...)

在测试中,您会将存根传递给它,并轻松测试它:

sendTemplatedEmail(stubOfSes)('some@person.com'...)

Google dependency injection 如果您需要更多理论知识。

2) 导出。为什么不使用 2 个道具导出对象:sesConstructorsendTemplatedEmail

let emailSender = {

  sesConstructor: () => {
    //...
    return AWS.SES(...);
  },

  sendTemplatedEmail: (emailTo, templateName, templateData) => {

    // ...
    return this.sesConstructor().sendTemplatedEmail(...);
  };

export default emailSender;

在这种情况下,在您的测试文件中,您只需要存根导入对象的 sesConstructor 方法,并使其易于测试。

我不认为它是如何组织的完整列表,但它是我使用它的方向。

希望对您有所帮助。