Iniciando com testes unitários no Create React App

  • Publicado em: 18/11/2020
  • Atualizado em: 08/12/2020

Não li, nem lerei

Um breve resumo para pessoas cansadas

Uma análise linha a linha baseada no teste padrão do arquivo src/App.test.js no Create React App.

Introdução

Se você chegou aqui, não tenho dúvidas que você sabe o que são testes unitários e sua importância, sendo assim, vou te deixar com dois parágrafos muito bons sobre testes automatizados para aplicações em React e logo em seguida vamos pro código.

Quando escrevemos testes automatizados para aplicações em React, provavelmente uma grande parte dos nossos testes será sobre algum componente de UI e não apenas funções de regras de negócio. Por isso, precisamos de uma biblioteca que nos auxilie a interagir com o DOM, renderizando e encontrando os elementos que precisamos verificar nos testes.

A React Testing Library surgiu com uma abordagem centrada no usuário e com padrões para enfatizar boas práticas de semântica e acessibilidade. E hoje já faz parte do pacote padrão de uma aplicação feita com o Create React App. - Eduarda Scharnhorst em React: Automatizando os testes em aplicações front-end.

Mão na massa

Imagine que você acabou de gerar uma aplicação com o CLI: Create React App.
Se você não sabe como fazer isso, visite a documentação clicando aqui.

A aplicação terá um arquivo src/App.js com a seguinte estrutura HTML(JSX):

<div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Edit <code>src/App.js</code> and save to reload. </p> <a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer" > Learn React </a> </header> </div>

E já terá um teste dentro do arquivo src/App.test.js. É importante explicar aqui que o nome do componente + .test dentro do mesmo diretório é uma convenção/boa prática pois a localização do teste fica mais intuitiva e não temos problemas de paths enormes em nossos imports.

Exemplificando, se você tem uma pasta chamada UI e dentro dela o componente Header.js seu teste será Header.test.js.
Outro uso comum seria ter uma pasta chamada Header e dentro dela o arquivo index.js, nesse caso seu arquivo de teste continuaria sendo Header.test.js, só mudaria o caminho do import;

Analisando o código

Vamos dar uma olhada no código inicial dentro de src/App.test.js.

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

Passo a passo

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

Estamos importando os métodos render e screen.

render é o método que permitirá renderizarmos nosso componente.
Imagine que você tem uma página vazia, logo, qualquer comando de teste rodado vai dar erro, correto?
Então, quando o render é chamado, ele renderiza o componente dentro desta página e estamos prontos para iniciar o teste.

screen é um método que possui queries dentro dele.
Isso quer dizer que usaremos funções/métodos que estão dentro de screen para capturar as informações que queremos.
Já vamos chegar lá nas próximas linhas.

import App from './App';

Importando nosso componente. Sem novidades.

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

test - que também pode ser encontrado como it -, é o método que nos permite escrever um texto descrevendo o que um teste bem sucedido deve fazer. No nosso exemplo, renderiza o link "learn react"

render(<App />);

Renderizando o componente App.

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

Atribuindo à constante linkElement a query(método) getByText que deve encontrar o elemento que possui o texto /learn react/i.

O trecho /learn react/i é um padrão regex que desabilita o case-sensitive, ou seja, ignora a diferença entre letras maiúsculas e minúsculas. Puro Javascript.

Lembrando que para selecionar elementos você pode continuar utilizando o bom e velho querySelector, por exemplo: const foo = container.querySelector('[data-foo="bar"]').

expect(linkElement).toBeInTheDocument();

expect é um comando que faz asserções. O que é isso?
Uma asserção pega um valor booleano (true || false) e sempre espera que o retorno seja true. Se o evento retornar falso, o teste falha.

Para exemplificar melhor, a linha acima também poderia ser escrita assim:
expect(screen.getByText(/learn react/i)).toBeInTheDocument();

toBeInTheDocument() é um matcher. O que é isso?
É uma função cujo valor resultante deve ser true em relação ao que está testando do expect. Neste caso é estarNoDocumento. Existe inclusive uma lista de matchers e você pode encontrá-la aqui.

Dificultando as coisas

Vamos realizar algumas alterações para podermos abordar outros métodos e discutir:

import { render, screen } from '@testing-library/react'; import App from './App'; describe('Componente App', () => { it('renders learn react link', () => { render(<App />); const linkElement = screen.getByText(/learn react/i); expect(linkElement).toBeInTheDocument(); }); it('renders learn react link as anchor', () => { render(<App />); const linkElement = screen.getByText(/learn react/i); expect(linkElement).toHaveAttribute('href'); }); });

describe é um método opcional que você pode utilizar para envelopar um conjunto de testes. Aqui estamos utilizando para delimitar os testes do nosso componente App e a resposta ao rodar os testes fica da seguinte maneira:

Resultado do teste

it, como falamos anteriormente está substituindo o test e pela minha experiência pessoal é o método mais utilizado no mercado.

it('renders learn react link as anchor', () => {

renders learn react link as anchor, novo teste, dessa vez, testaremos se o elemento é um link

expect(linkElement).toHaveAttribute('href');

toHaveAttribute('href'), conferindo se o elemento tem um href.

E qual é o problema que temos aqui?

O teste vai passar com qualquer elemento que tenha um href, por exemplo, se for uma div o teste vai retornar com sucesso.
O correto seria:

it('renders learn react link as anchor', () => { render(<App />); // adicionando `.closest('a')` const linkElement = screen.getByText(/learn react/i).closest('a'); expect(linkElement).toHaveAttribute('href'); });

Então esse último tópico foi gratuito só para chamar atenção em como você realiza seus testes e mostrar que 100% de cobertura não quer dizer uma aplicação sem bugs. 🙂

Referências

Felipe Marciano
Sobre o autor
Felipe Marciano

dev front-end, viciado em café, cinema e abraçar cachorros.

Tem perguntas,fale comigo.Respondo assim que puder.
Para mandar um "Oi" 👋felipexperto@gmail.com
Me siga no LinkedIn 🚀linkedin.com/in/felipexperto/
© 2024, Construído com Gatsby