首页 > 解决方案 > Sinon 如何对非导出的私有功能进行单元测试?

问题描述

我对单元测试真的很陌生,我试图使用 模拟aws.ses方法sinon.js,但我关心的是我如何调用该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();
    });

   
  });
});

标签: javascriptunit-testingdependency-injectionsinon

解决方案


实际上,您的sendTemplatedEmail函数还做了一件未在名称中指明的事情:初始化传输。并且很难依赖sendTemplatedEmail. 您需要与此依赖项有关,以使其更具可测试性,可能的解决方案:

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

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

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

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

closure如果它适合您,您也可以将实例放入:

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

然后你会使用它:

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

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

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

dependency injection如果您需要更多关于它的理论,请谷歌。

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

let emailSender = {

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

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

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

export default emailSender;

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

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

希望能帮助到你。


推荐阅读