Синон, как юнит-тестирование неэкспортированной приватной функциональности? - PullRequest
0 голосов
/ 03 мая 2018

Я действительно новичок в модульном тестировании, я пытаюсь смоделировать aws.ses метод, используя sinon.js, но меня беспокоит то, как я вызываю метод sesConstructor. Поскольку он не экспортируется из ses.js, ive объявил метод 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();
    });

   
  });
});

1 Ответ

0 голосов
/ 04 мая 2018

На самом деле, ваша функция 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) Экспортируйте его. Почему бы вам не экспортировать объект с двумя реквизитами: sesConstructor и sendTemplatedEmail?

let emailSender = {

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

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

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

export default emailSender;

В этом случае в вашем тестовом файле вам просто нужно указать метод импортируемого объекта sesConstructor и получить его легко тестируемым.

Не думаю, что это полный список того, как это можно организовать, но это те направления, с которыми я бы работал.

Надеюсь, это поможет.

...