Capítulo 16: Testes no Front-End com Vitest e React Testing Library
Especialização Frontend com React & TypeScript • React 19, Hooks, Estado Global e Next.js
🗺️ Mapa Conceitual do Tópico
flowchart TD
TEST["Teste RTL (Simula Usuário Real)"] --> QUERY["screen.getByRole('button', { name: /salvar/i })"]
QUERY --> EVENT["await user.click(button)"]
EVENT --> ASSERT["expect(screen.getByText(/sucesso/i)).toBeInTheDocument()"]
🏛️ 1. A Filosofia da React Testing Library (RTL)
A React Testing Library revolucionou os testes de interface ao abandonar os testes de detalhes de implementação (como inspecionar o estado interno ou os métodos de classe) em favor de testar a aplicação exatamente da mesma forma que o usuário final e a tecnologia assistiva interagem com ela:
“The more your tests resemble the way your software is used, the more confidence they can give you.”
🎯 2. Hierarquia de Prioridade das Queries de Busca
Para maximizar a acessibilidade e a robustez dos testes contra refatorações de markup, siga a ordem de preferência oficial da RTL:
- Queries Acessíveis a Todos (Prioridade Máxima):
getByRole: Busca por papel semântico (button,heading,checkbox,dialog).getByLabelText: Busca campos vinculados a labels.getByPlaceholderText: Busca por placeholders de input.getByText: Busca por textos visíveis na tela.
- Queries Semânticas:
getByAltText: Busca imagens por texto alternativo.getByTitle: Busca por títulos de atributo.
- Escotilha de Fuga (Escape Hatch):
getByTestId: Utilizar apenas quando o elemento não possuir representação semântica ou papel acessível no DOM.
📊 3. Comparativo: Vitest vs Jest
| Recurso | Vitest (Moderno) | Jest (Legado) |
|---|---|---|
| Integração com Vite | ✅ Nativa (compartilha a mesma config) | ❌ Requer configuração duplicada |
| Suporte Nativo a ESM | ✅ Nativo e transparente | ⚠️ Problemático e exige flags |
| Velocidade de Execução | Ultrarrápida com workers multithread | Mais pesada em cold start |
| Compatibilidade de API | 100% compatível com a API de describe/it/expect |
Padrão histórico |
⚠️ 4. Armadilhas Comuns e Anti-Padrões de Produção
- Uso de
fireEventem vez deuserEvent: OfireEventdispara apenas o evento sintático direto, ignorando a sequência natural de foco, clique e teclado que o navegador executa. Use sempre@testing-library/user-event. - Esquecer de
awaitem Chamadas deuserEvent: Todos os métodos douserEventmoderno são assíncronos. Esquecer oawaitgera testes com falsos positivos (flaky tests).
💡 5. Diretrizes de Engenharia (Meta & React Docs)
- Evite Testar Detalhes de Implementação: Não teste se um hook específico foi invocado ou qual é o valor exato de uma variável de estado interna. Teste o que aparece ou desaparece visualmente na tela após a ação do usuário.
- Prefira Asserções com
jest-dom: Utilize matchers expressivos como.toBeInTheDocument(),.toBeDisabled()e.toHaveValue().
💻 6. Demonstração Arquitetural do Conceito
// ToggleComponent.test.tsx - Teste acessível e robusto
import React, { useState } from "react";
import { describe, it, expect } from "vitest";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
function VisibilityToggle() {
const [visible, setVisible] = useState(false);
return (
<div>
<button onClick={() => setVisible(v => !v)}>Alternar</button>
{visible && <p>Conteúdo confidencial exibido.</p>}
</div>
);
}
describe("VisibilityToggle", () => {
it("deve alternar a visibilidade do conteúdo ao clicar no botão", async () => {
const user = userEvent.setup();
render(<VisibilityToggle />);
expect(screen.queryByText(/conteúdo confidencial/i)).not.toBeInTheDocument();
await user.click(screen.getByRole("button", { name: /alternar/i }));
expect(screen.getByText(/conteúdo confidencial/i)).toBeInTheDocument();
});
});
📋 7. Cheat-Sheet das Variantes de Query da RTL
| Variante | Quando o elemento não existe? | Suporta Assíncrono? | Quando Utilizar? |
|---|---|---|---|
getBy... |
Lança Exceção | ❌ Não | Elementos que obrigatoriamente devem estar na tela. |
queryBy... |
Retorna null |
❌ Não | Assertar que um elemento NÃO deve estar na tela. |
findBy... |
Rejeita Promise | ✅ Sim (await) |
Elementos que surgem após chamadas assíncronas/APIs. |
🔍 8. Diagnóstico de Performance & React DevTools Profiler
Velocidade e cobertura no pipeline:
- Tempo de Execução: Suites de teste com Vitest e happy-dom executam em menos de 10ms por arquivo de teste.
- Relatório de Cobertura V8: Execute
vitest --coveragepara inspecionar branches e linhas não cobertas diretamente no terminal.
🧪 9. Casos de Teste e Cenários de Borda Recomendados
- 🔍 Elementos Ocultos no DOM: Valide a diferença entre elementos presentes com
display: nonevs elementos desmontados. - 🔍 Simulação de Digitação Longa: Teste o preenchimento de inputs com strings longas para avaliar a performance do
userEvent. - 🔍 Asserções em Modais: Valide o fechamento de modais tanto pelo botão de fechar quanto pelo clique no backdrop e tecla Esc.