【问题标题】:Sinon how to unit test a non exported private functionality?Sinon 如何对非导出的私有功能进行单元测试?
【发布时间】:2018-05-03 04:49:27
【问题描述】:

我对单元测试真的很陌生,我试图使用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();
    });

   
  });
});

【问题讨论】:

    标签: javascript unit-testing dependency-injection sinon


    【解决方案1】:

    实际上,您的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 方法,并使其易于测试。

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

    希望对你有帮助。

    【讨论】:

      猜你喜欢
      • 2021-10-12
      • 2019-07-06
      • 2019-06-04
      • 1970-01-01
      • 2016-12-21
      • 2017-09-11
      • 1970-01-01
      • 2020-11-01
      • 1970-01-01
      相关资源
      最近更新 更多