Executando verificação de segurança...
1

Ótimo ponto abordado e explicação muito assertiva, mas fiquei com uma dúvida, quando você disse a respeito de tornar a função mais testavel logo me veio a mente o TDD e pensando por esse lado não dificultaria mais o processo de escrever os testes?
Acho que é uma ótima medida pra uma refatoração no código posteriormente mas não sei se a melhor das óticas para encarar o desenvolvimento desses tipos de funções.
ps: sou só um estudante de programação, me perdoa se falei groselha

Carregando publicação patrocinada...
1

Pelo contrário, injeção de dependência anda de mãos dadas com TDD, principalmente se a gente estiver desenvolvendo "de cima para baixo".

Por exemplo, vamos voltar pro exemplo do payWithRetry do post:

const payWithRetry = async (payRetryTimes, creditCard) => {
  let times = 1;
  while (true) {
    try {
      await pay(creditCard);
      return;
    } catch(error) {
      if(times < payRetryTimes) {
        times++;
        continue;
      }
      
      throw error;
    }
  }
};

Imagine que neste momento a gente ainda não tem a implementação do pay (pois estamos desenvolvendo "de cima pra baixo") e, antes de ter escrito esta implementação, a gente tivesse começado com o seguinte teste:

describe("When pay fails more times than `payRetryTimes`", () => {
  it("Throws error", () => {
    const creditCard = {
      //...
    };

    expect(payWithRetry(3, creditCard)).rejects.toThrow();
  });
});

Como é que a gente faz pra de fato testar este caso específico do payRetryTimes?

O primeiro obstáculo é que pay ainda não está implementado e, segundo, mesmo que estivesse, como ele chama um serviço externo, não é fácil fazer forçar a falha dele pra que nós possamos testar este caso.

É aí que entra a injeção de dependência.

Primeiro a gente ajusta a implementação de payWithRetry pra receber pay como parâmetro:

export const makePayWithRetry =
  ({ pay }) =>
  async (payRetryTimes, creditCard) => {
    let times = 1;
    while (true) {
      try {
        await pay(creditCard);
        return;
      } catch (error) {
        if (times < payRetryTimes) {
          times++;
          continue;
        }

        throw error;
      }
    }
  };

Depois, ajustamos nosso teste pra passarmos uma versão mockada de pay que vai se comportar exatamente como precisamos para o nosso teste:

describe("When pay fails more times than `payRetryTimes`", () => {
  it("Throws error", () => {
    const payMock = jest.fn().mockImplementation(() => {
      throw new Error("");
    });
    const creditCard = {
      //...
    };

    const payWithRetry = makePayWithRetry({ pay: payMock });

    expect(payWithRetry(3, creditCard)).rejects.toThrow();
  });
});

Veja que, desta maneira, nós podemos testar a lógica do payWithRetry mesmo sem termos a implementação do pay.