Introdução

Código React sem testes é um passivo. Você refatora um componente e algo quebra em três telas de distância. O Jest é o framework de testes que vem incluído no Create React App e se integra estreitamente com o React Testing Library. Ele cuida do executor de testes, asserções, mocks e relatórios de cobertura em um único pacote.

O que é o Jest?

Jest é um framework de testes open-source desenvolvido pelo Facebook para aplicações JavaScript. Ele exige quase nenhuma configuração para começar, o que é raro entre ferramentas de teste.

Principais Funcionalidades do Jest

  • Zero Configuração: O Jest exige configuração mínima, permitindo que você comece a testar rapidamente.
  • Snapshot Testing: Captura a saída renderizada de componentes React para detectar mudanças na UI.
  • Testes Isolados: Cada teste roda em seu próprio sandbox para evitar interferência entre testes.
  • Mocking Poderoso: Faça mock de funções, módulos e timers para controlar o comportamento do código durante os testes.
  • Cobertura de Código: Gere relatórios detalhados para entender quanto do seu código está sendo testado.

Por Que Usar Jest para Testar Aplicações React?

O Jest se combina naturalmente com o React Testing Library, que permite consultar e interagir com seus componentes da forma como um usuário faria, em vez de depender de detalhes de implementação. Os testes rodam em paralelo, então mesmo suites grandes terminam rapidamente. A API de mocking é completa o suficiente para isolar qualquer dependência.

Configurando o Jest em uma Aplicação React

Pré-requisitos

Node.js e npm instalados, com familiaridade básica com React e JavaScript.

Criando um Novo App React

Crie uma nova aplicação React usando o Create React App:

npx create-react-app my-app
cd my-app

O Create React App vem com o Jest pré-configurado, então você pode começar a escrever testes imediatamente.

Aplicações React Existentes

Se você tem um projeto React existente sem Jest, instale-o:

npm install --save-dev jest babel-jest @babel/preset-env @babel/preset-react react-test-renderer

Em seguida, adicione o seguinte ao seu package.json:

"scripts": {
  "test": "jest"
},
"jest": {
  "setupFilesAfterEnv": ["<rootDir>/src/setupTests.js"]
}

Escrevendo Seu Primeiro Teste

Criando um Componente React

Crie um novo arquivo src/components/Greeting.js:

import React from 'react';

function Greeting({ name }) {
  return <h1>Hello, {name}!</h1>;
}

export default Greeting;

Escrevendo um Teste para o Componente

Crie um arquivo de teste src/components/Greeting.test.js:

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

test('renders greeting message', () => {
  render(<Greeting name="World" />);
  const greetingElement = screen.getByText(/Hello, World!/i);
  expect(greetingElement).toBeInTheDocument();
});

Executando o Teste

npm test

O Jest encontra os arquivos de teste automaticamente e exibe os resultados no terminal.

Entendendo a Sintaxe dos Testes

O teste acima segue um padrão simples. Importe o componente, renderize-o em um DOM virtual via render(), consulte um elemento com screen.getByText() e verifique sua existência com expect().toBeInTheDocument(). A função test() envolve cada caso com uma string descritiva e um callback.

Testando Interações do Usuário

Exemplo: Evento de Clique em Botão

Suponha que temos um botão que incrementa um contador:

// Counter.js
import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  );
}

export default Counter;

Escrevendo Testes para Interação do Usuário

// Counter.test.js
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import Counter from './Counter';

test('increments count on button click', () => {
  render(<Counter />);
  const buttonElement = screen.getByText(/Click me/i);
  fireEvent.click(buttonElement);
  expect(screen.getByText(/You clicked 1 times/i)).toBeInTheDocument();
});

fireEvent.click() simula um clique real e, em seguida, verificamos que o contador exibido foi atualizado. O teste falharia se o handler do onClick estivesse quebrado ou se a atualização de estado não disparasse uma re-renderização.

Snapshot Testing

O snapshot testing registra a saída renderizada de um componente e falha se ela mudar inesperadamente. É útil para detectar regressões visuais não intencionais.

Criando um Teste de Snapshot

// Greeting.test.js
import React from 'react';
import renderer from 'react-test-renderer';
import Greeting from './Greeting';

test('Greeting component matches snapshot', () => {
  const tree = renderer.create(<Greeting name="World" />).toJSON();
  expect(tree).toMatchSnapshot();
});

A primeira execução cria um arquivo de snapshot. Todas as execuções subsequentes comparam com ele. Quando você alterar intencionalmente a saída, atualize o snapshot com jest --updateSnapshot.

Mockando Funções e Módulos

Mockando um Módulo

Suponha que seu componente usa um módulo de API:

// api.js
export const fetchData = () => {
  // fetch data from an API
};

Faça o mock nos seus testes para que chamadas de rede nunca sejam efetivamente realizadas:

jest.mock('./api', () => ({
  fetchData: jest.fn(() => Promise.resolve({ data: 'mock data' })),
}));

Usando o Mock em um Teste

// DataComponent.test.js
import React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import DataComponent from './DataComponent';
import { fetchData } from './api';

test('displays fetched data', async () => {
  fetchData.mockResolvedValueOnce({ data: 'mock data' });
  render(<DataComponent />);
  await waitFor(() => expect(screen.getByText(/mock data/i)).toBeInTheDocument());
});

Dicas para Testes Eficazes

  • Teste seu próprio código, não bibliotecas de terceiros.
  • Escreva testes determinísticos. Qualquer teste que produza resultados diferentes em execuções diferentes acabará minando a confiança na suite.
  • Teste a saída que o usuário vê, não os detalhes de implementação internos. Se você refatorar os internos sem alterar o comportamento, nenhum teste deve quebrar.
  • Use nomes descritivos para os testes. "increments count on button click" é mais útil do que "test 1".

Relatórios de Cobertura de Código

O Jest gera relatórios de cobertura que mostram quais linhas, branches e funções seus testes exercitam.

Habilitando a Cobertura de Código

npm test -- --coverage

Revise a saída para encontrar caminhos não testados, especialmente branches de erro que só são acionados em condições específicas.

Integração com Integração Contínua (CI)

Execute o Jest no seu pipeline de CI para que cada pull request inclua um gate de testes.

Exemplo com GitHub Actions

Crie um arquivo .github/workflows/main.yml:

name: Node.js CI

on:
  push:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [14.x, 16.x]
    steps:
      - uses: actions/checkout@v2
      - name: Use Node.js $
        uses: actions/setup-node@v2
        with:
          node-version: $
      - run: npm install
      - run: npm test -- --coverage
      - uses: actions/upload-artifact@v2
        with:
          name: coverage-report
          path: coverage/

Conclusão

O Jest cobre tudo que um fluxo de trabalho de testes React precisa: um executor de testes paralelo e rápido, uma biblioteca de asserções, mocking, diff de snapshots e relatórios de cobertura. Comece com os testes de interação — eles capturam mais regressões por linha de código de teste escrito — e depois adicione snapshots para componentes de UI estáveis. Assim que os testes rodarem automaticamente no CI, você terá o loop de feedback permanente que torna a refatoração segura.

Recursos Adicionais