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

时间:2018-05-03 04:49:28

标签: javascript unit-testing dependency-injection sinon

我真的很擅长单元测试,我试图使用aws.ses模拟sinon.js方法,但我关心的是如何调用sesConstructor方法。由于它未从ses.js导出,因此我在测试套件中声明了sesConstructor方法。

如果有人能告诉我这是否是一个完整的反模式以及是否还有其他更好的解决方案(不使用'重新连接'模块),我将非常感激

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();
    });

   
  });
});

1 个答案:

答案 0 :(得分:0)

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

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

希望它有所帮助。