Felipe Cesar

    Como criar um componente React com TDD

    Quando começamos a estudar sobre TDD (Test Driven Development) é comum surgirem algumas dúvidas, por exemplo, o que testar? como escrever testes antes do código de produção?

    Este é um artigo bem prático que tem como objetivo acabar com algumas dessas dúvidas se tratando de aplicações React.

    Test Driven Development

    O Test Driven Development ou TDD significa Desenvolvimento Orientado a Testes e é uma prática que consiste em um ciclo curto de três passos, conhecidos como Red/Green/Refactor.

    TDD Flow

    • Red: Primeiro escrevemos um teste que falha, para alguma funcionalidade que ainda será desenvolvida.
    • Green: Com o teste criado, escrevemos uma solução simples para fazê-lo passar.
    • Refactor: Por último refatoramos, ou seja, melhoramos o código.

    Esse ciclo deve se repetir várias vezes durante todo o desenvolvimento.

    Tendo isso em mente podemos ver como esse ciclo funciona na prática.

    Escrevendo um teste que falha

    Para acompanhar o desenvolvimento você pode baixar o repositório e fazer o checkout na branch exercise-01-start.

    Após baixar o código, crie a pasta src/components e adicione o arquivo Highlight.test.js com o seguinte conteúdo:

    import ReactDOM from "react-dom";
    import Highlight from "./Highlight";
    
    test("renders a value", () => {
      const container = document.createElement("div");
      document.body.appendChild(container);
    
      ReactDOM.render(<Highlight />, container);
    
      expect(document.body.textContent).toBe("3000");
    });

    A função test recebe uma descrição do teste como primeiro parâmetro. É uma boa prática sempre começar com um verbo no presente. O segundo parâmetro é uma função anônima com o código do teste.

    Uma const chamada container tem como valor uma div, que é o elemento onde o componente será renderizado.

    O método render do ReactDOM é utilizado para renderizar o componente.

    Por último, é feita uma chamada a função expect, ela fornece uma lista de métodos que nos permitem fazer diferentes asserções. Neste caso, verificamos se o textContent da página é 3000.

    Execute o comando npm test, veja que o teste está falhando, isso já era esperado, porque ainda estamos no primeiro passo do ciclo.

    Fazendo o teste passar

    Agora crie o arquivo Highlight.js dentro de src/components, com o seguinte conteúdo:

    const Highlight = () => <div>3000</div>;
    
    export default Highlight;

    Por enquanto não precisamos de nada além disso para que o teste passe.

    Refatorando o código

    Adicionamos um valor "na mão", apenas para o teste passar, mas vamos precisar que o componente funcione com outros valores, para isso vamos fazer a seguinte alteração no teste:

    ReactDOM.render(<Highlight value="3000" />, container);

    E logo em seguida no componente:

    const Highlight = ({ value }) => <div>{value}</div>;

    Fizemos essas mudanças na intenção ter um código melhor, que funcione com diferentes valores, mas quem garante que funciona?

    Repetindo o ciclo

    Para garantir que o componente está funcionando como esperado, podemos repetir o ciclo escrevendo outro teste. Adicione o seguinte código no arquivo Highlight.test.js:

    test("renders another value", () => {
      const container = document.createElement("div");
      document.body.appendChild(container);
    
      ReactDOM.render(<Highlight value="5000" />, container);
    
      expect(document.body.textContent).toBe("5000");
    });

    Execute os testes novamente. Note que o segundo teste falha e com um erro bem estranho:

    Expected substring: "5000"
    Received string:    "30005000"

    Isso acontece porque adicionamos um elemento ao body e não o removemos após a execução do primeiro teste.

    Removendo efeitos colaterais

    Para que o teste passe, devemos garantir que o que foi feito em um não interfira no resultado do outro. Podemos remover todos os elementos do body após cada teste. A função afterEach do Jest permite fazer isso de uma forma bem simples. Adicione o seguinte código antes dos testes:

    afterEach(() => {
      document.body.innerHTML = "";
    });

    Removendo código duplicado

    Se olharmos bem o arquivo de teste, podemos ver claramente que alguns itens se repetem. Essa é a hora em que devemos resistir a tentação de passar para o próximo componente e trabalhar duro para deixar nosso código o mais limpo possível.

    Crie a seguinte função no arquivo de testes:

    function render(component) {
      const container = document.createElement("div");
      document.body.appendChild(container);
    
      ReactDOM.render(component, container);
    }

    Ela contém o código que se repete nos dois testes. Com essa função, podemos refatorar os testes, tornando-os mais simples:

    test("renders a value", () => {
      const value = "3000"; // Arrange
      render(<Highlight value={value} />); // Act
      expect(document.body.textContent).toBe(value); // Assert
    });
    
    test("renders another value", () => {
      const value = "5000"; // Arrange
      render(<Highlight value={value} />); // Act
      expect(document.body.textContent).toBe(value); // Assert
    });

    Para saber se um teste é bom, você deve ser capaz de identificar cada uma das seguintes etapas:

    • Arrange: Faz o setup das dependências de teste
    • Act: Executa o código de produção em teste
    • Assert: Verifica se as expectativas são atendidas

    Mas isso não é tudo, podemos deixar os testes ainda melhores, garantindo que eles atendam alguns requisitos:

    • Sejam descritivos
    • Independentes de outros testes
    • Não tenham efeitos colaterais

    O ideal é sempre buscar atender todos esses requisitos, você vai ganhar muito com isso e provavelmente evitar algumas dores de cabeça no futuro.

    Conclusão

    Nesse artigo desenvolvemos um componente React seguindo o TDD, fiz o possível para que não ficasse muito longo.

    Se esse conteúdo te ajudou ou se ficou alguma dúvida, deixa um comentário, isso me ajuda a saber se devo criar mais conteúdo desse tipo.

    Ah! O código completo pode ser encontrado nesse repositório. Abraço!