Testes em ReactJS – o que é preciso para iniciar?

Há um tempo atrás eu ouvi a seguinte frase: “Prazos são negociáveis, qualidade não!”. Isso me fez questionar algumas coisas, incluindo o que eu sabia sobre testes e a importância deles. Então, para sanar os questionamentos sobre como eles podem ser aplicados no ReactJS trouxe algumas informações sobre React, Jest e testing library.

Jessica Meira

Jessica Meira

April 27, 2021 | leitura de 8 minutos

dev

Antes de começar, caso você ainda não esteja familiarizado com o assunto, há um artigo bem legal aqui no blog explicando sobre a importância dos testes unitários para garantir a qualidade do desenvolvimento de softwares. Outro artigo que eu gostaria de recomendar é: "O que você precisa saber sobre JavaScript antes de começar no React", ainda mais se você tem dificuldade de entender a sintaxe de versões mais modernas de JavaScript.

Agora que você já está craque sobre a tecnologia que será abordada aqui, que é o React, vamos  partir para a compreensão dos testes no ReactJS e a maneira como podemos escrevê-los, entendendo alguns conceitos e suas estruturas.

ReactJS e testes

Em resumo, o React faz com que a árdua função de criação de views seja uma tarefa fácil. Ele irá atualizar e renderizar de forma eficiente apenas os componentes necessários na medida em que os dados mudam, fazendo com que seu código seja mais previsível e melhor aproveitado (ou reaproveitado).

A escolha da ferramenta precisa estar diretamente ligada à expectativa real. Usualmente as mais recomendadas são:

  • React testing library: É um conjunto de utilitários que facilitam as consultas à DOM exatamente como um usuário faria, ou seja, é capaz de encontrar elementos de formulários, botões e links, entre outros. É importante se atentar que essa lib não é um test runner ou um framework, tanto que é altamente recomendável a utilização do Jest para isso. Mesmo assim, ainda há a opção de utilizá-la sem o jest. A própria documentação disponibiliza um tutorial de como isso é possível.

  • Jest: Esse framework foi criado pelo Facebook e tem como uma de suas principais qualidades a velocidade e facilidade em execução, já que ele visa trabalhar de uma forma simples, não há necessidade de configuração na maioria dos projetos, principalmente para o React. Ainda conta com a facilidade de lidar com snapshots e para melhorar o seu desempenho, cada teste é realizado de forma isolada.

Como funcionam as ferramentas de teste do ReactJS

As ferramentas de teste não irão necessariamente entender as regras ou os processos do seu projeto. O intuito de utilizar um teste é validar a funcionalidade e renderização dos componentes, se há determinado botão ou link na DOM e se o click está funcionando da maneira correta ou esperada.

Para um projeto pequeno talvez a ideia de teste seja um pouco trabalhosa e muitas vezes ignorada, mas na medida que o projeto cresce e novas features são desenvolvidas, você provavelmente agradecerá quem o iniciou, já que se torna praticamente inviável e inseguro criar os testes quando tudo já está tão avançado e há tantos processos novos para se testar.

Exemplos práticos de utilização 

Para exemplificar o testes no React, vou seguir com a explicação criando um projeto quase do zero com comando npx create-react-app.

Normalmente, após a criação do projeto há uma organização de estruturas, mas como neste caso veremos apenas o conceito, iremos nos concentrar apenas no arquivo de teste dentro pasta src App.test.js

De começo precisamos entender o que está acontecendo nesse arquivo:

import { render, screen } from  '@testing-library/react';

import App from  './App';

test('renders learn react link', () => {

 render(<App  />);

 const linkElement = screen.getByText(/learn react/i);

 expect(linkElement).toBeInTheDocument();

});

A primeira coisa a notar nele é a importação da biblioteca @testing-library que é a responsável por interagir com a DOM. Por padrão vem a importação do render e do screen, que não vão exatamente apresentar a tela, mas sim capturar os elementos.

Depois, o Import do componente a ser testado, que no nosso arquivo de exemplo é o App.

A Sintaxe básica para esse arquivo de teste é:

test('My testname', () ={

 // steps to reproduce

});

O método test() - é o responsável por chamar os testes a serem executados e, dentro desse método, o primeiro argumento dará nome ao seu teste. Após, vem a função que contém as expectativas, ou seja, o que é pra acontecer e o que não é para acontecer dependendo do que você irá testar. Ainda nesse método há alguns outros argumentos, mas que não serão abordados por enquanto.

Dentro da função, será informado o passo a passo do que se deseja que aconteça. 

  1. É renderizado o componente

  2. Criado uma variável (linkElement) 

  3. A variável recebe do método screen o elemento que possuir o texto learn react 

  4. O esperado é que a variável esteja no documento.

Para rodar o teste utiliza-se o comando: Yarn test ou npm test. Como não foi alterado nenhuma informação, ao rodá-lo no terminal será apresentada uma tela de PASS, informando a quantidade e quais os testes presentes, quais deles passaram e falhas, caso existam.

Para mudar um pouco e entender um pequeno comportamento com os testes, vou adicionar um checkbox no meu app:

<div>

         <label  htmlFor="checkbox">Check</label>

         <input  id="checkbox"  type="checkbox"  />

       </div>

E no arquivo de teste posso inserir outra simulação, dessa vez com uma ação do usuário, como um click:

test('renders checkbox test', () => {

 render(<App  />);

 userEvent.click(screen.getByText('Check'))

 expect(screen.getByLabelText('Check')).toBeChecked()

});

Para que esse teste rode com sucesso é necessário importar o userEvent:

import userEvent from  '@testing-library/user-event';

O user-event é uma biblioteca complementar que nos permite executar ações mais avançadas. A testing library fornece o método fireEvent integrado que também nos permite executar algumas simulações, porém o mais recomendável para isso é o user-event.

Se o teste for executado novamente, os dois testes passarão.

Além desses pontos, algumas outras funcionalidades muito utilizadas em qualquer teste são as mocks e o snapshot.

Mocks

Mocks são utilizadas para simular chamadas reais, ou seja, permitem criar módulos, funções com retorno de dados que você possa controlar (normalmente, é preferível que sejam usados dados "falsos" para testes a fim de evitar a lentidão e a inconstância) e simular uma dependência. Elas facilitam essas chamadas onde a implementação não seria viável, no caso dos testes, por exemplo. 

Alguns modelos da sintaxe para a sua utilização:

//function

const mockFn = jest.fn();

mockFn();

expect(mockFn).toHaveBeenCalled();

//function 2

import getName from  '../getusername'

const mockFname = {}

mockFname.getSomething = jest.fn()

mockFname.getSomething.mockReturnValue('Ateliware')

test('identificar nome do usuario, () => {

 expect(getName(mockFname)).toBe('Ateliware')

 expect(mockFname.getSomething.mock.calls.length).toBe(1)

})

//module

jest.mock('../moduleName', () => {

 return jest.fn(() =>  9);

});

const moduleName = require('../moduleName');

moduleName(); // return '9';

Snapshots

Com testes de snapshots você é capaz de prevenir alterações indesejadas na UI do projeto. Então, podemos dizer que é uma maneira de prevenir bugs de layout, uma vez que você salva o componente em texto e informa que o resultado dessa renderização deve ser exatamente a mesma sempre.

Para salvar a renderização, primeiro há a importação do react-test-renderer. Caso você se depare com algum erro para identificar o módulo pode instalá-lo com npm install react-test-renderer.

import renderer from  'react-test-renderer';

O React test renderer é um pacote que fornece um renderizador de componentes que não depende da DOM. Ele auxilia na captura de snapshots.

const component = renderer.create(<App  />);

 const tree = component.toJSON();

 expect(tree).toMatchSnapshot();

Note que eu estou passando o meu componente como argumento para o renderer e depois convertendo para json.

Os snapshots são geralmente salvos em uma pasta snapshots e o arquivo é similar a esse:

testes-em-reactjs.png

Por último, podemos abordar brevemente os testes assíncronos, que são uma prática muito comum. Nesses casos, o Jest precisa saber quando o código que está em teste é concluído antes de passar para outra avaliação. O Jest tem algumas maneiras de lidar com isso, vamos retratar duas aqui? A primeira delas é informando a palavra done.

Como no nosso exemplo estamos lidando com um callback, sempre que possível é interessante poder ver o log no caso de falha, então para isso precisaremos encapsular o expect em um bloco try e passar o erro no bloco catch para o done. Exemplo:

test('esperando pelo callback', done => {

 function callback(data) {

   try {

     expect(data).toBe('retorno');

     done();

} catch (error) {

     done(error);

   }

 }

 fetchData(callback);});

Outra maneira seria utilizando o async/await nos testes, que já são bem conhecidos no javascript. Para escrever um teste assíncrono, basta usar a palavra-chave async na frente da função, e aguardar os dados com o await. Exemplo:

//async - await

test('waiting return', async () => {

 const data = await fetchData();

 expect(data).toBe('Ateliware');

});

Agora que alguns dos conceitos mais importantes dos testes no react foram apresentados, já é possível realizar a construção de testes simples. Além dos métodos apresentados aqui no post, há um mundo muito mais profundo com diversas outras possibilidades dentro dos utilitários para testes.

Conclusão

A Elaboração de testes para o ReactJS muitas vezes pode parecer complicada, mesmo que nos exemplos apresentados aqui todas as utilizações foram simples. Vale lembrar que cada projeto tem regras diferentes e maneiras diferentes para elaboração de testes. Alguns serão apenas testes de renderização correta dos componentes, outros terão os testes feitos a fim de validar a funcionalidade do componente. 

Independente da usabilidade, os testes poderão validar e prevenir os erros na aplicação, garantindo, assim, a tranquilidade no decorrer do desenvolvimento, pois caso alguma funcionalidade apresente um comportamento inesperado após o desenvolvimento de uma nova feature, será mais fácil de identificar com os testes.

Por enquanto é isso! Espero que tenha sido útil aos seus novos conhecimentos e caso você ainda não tenha criado nenhum teste, nunca é tarde pra começar a garantir a qualidade do seu projeto.

Referências:
- Testing Library
- Jest
- React - Renderizador de teste
Jessica Meira
Jessica Meira

Software Engineer | Cursando pós-graduação em Inteligência Artificial, um pouco curiosa nas horas vagas. Mais de 15 anos na área de Tecnologia e apaixonada por música e instrumentos.

LinkedIn