Communitygithub.com

Testes React

Testes de componentes React com React Testing Library, Vitest/Jest, MSW para simulação de rede, asserções de acessibilidade com axe e a fronteira de decisão entre testes de componentes e testes end-to-end com Playwright/Cypress. Use ao escrever ou corrigir testes para componentes, hooks ou páginas React.

O que é Testes React?

Esta skill fornece um guia completo para testes de componentes React usando React Testing Library, com foco em comportamento em vez de detalhes de implementação. Inclui padrões para testar componentes, hooks personalizados, simulação de requisições HTTP com MSW e asserções de acessibilidade com axe. A skill também estabelece diretrizes claras sobre quando usar Vitest/Jest versus Playwright/Cypress para testes de componentes ou E2E completos. Suporta os frameworks Vitest e Jest, e recomenda o uso de `userEvent` para simular interações realistas do usuário. Além disso, oferece um workflow TDD (Red-Green-Refactor) e cobertura de metas por tipo de código (utilitários, hooks, componentes e páginas), além de anti-padrões a serem evitados.

Funciona com✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/react-testing

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

O que Testes React faz?

Padrões abrangentes de teste React para testes de componentes focados em comportamento, testes de hooks personalizados, asserções de acessibilidade e simulação em nível de rede.

Quando Ativar

  • Escrevendo testes para componentes React, hooks personalizados ou páginas
  • Adicionando cobertura de teste a componentes legados não testados
  • Migrando de Enzyme ou padrões da era de classe para React Testing Library
  • Configurando Vitest ou Jest para um novo projeto React
  • Simulando requisições HTTP em testes
  • Afirmando violações de acessibilidade
  • Decidindo quais testes pertencem ao RTL vs Playwright Component Testing vs E2E completo

Princípio Central

Teste o que o usuário vê e faz, não os detalhes de implementação.

Um teste deve:

  • Renderizar o componente com os mesmos provedores que ele tem em produção
  • Interagir com ele por meio de consultas acessíveis (role, label) e userEvent
  • Afirmar saída visível e efeitos colaterais observáveis (callback disparado, requisição enviada)

Um teste NÃO deve:

  • Inspecionar estado do componente, props passadas para filhos ou quais hooks foram chamados
  • Simular o próprio React ou hooks do framework
  • Afirmar sobre o número de renderizações ou estrutura do DOM além do que afeta os usuários

Escolha da Biblioteca

ExecutorQuandoNota
VitestVite, Remix, configurações modernasMais rápido, ESM nativo, API compatível com Jest
JestNext.js, CRA, repositórios estabelecidosPadrão para muitos projetos React
Playwright Component TestingMotor de navegador real necessárioUse quando o JSDOM não tem o recurso necessário
Cypress Component TestingNavegador real, Cypress já em usoAlternativa ao Playwright CT

Escolha um. Não execute RTL + Vitest E Playwright CT no mesmo repositório, a menos que você tenha uma separação clara de responsabilidades.

Prioridade de Consulta

O React Testing Library expõe consultas em três níveis — use de cima para baixo:

  1. Acessível a todos: getByRole, getByLabelText, getByPlaceholderText, getByText, getByDisplayValue
  2. Semântico: getByAltText, getByTitle
  3. IDs de Teste (escotilha de escape): getByTestId
// Melhor
screen.getByRole("button", { name: /salvar/i });

// OK para inputs
screen.getByLabelText("Email");

// Último recurso
screen.getByTestId("save-btn");

Variantes:

  • getBy* — lança exceção se não houver correspondência
  • queryBy* — retorna null (use para "afirmar ausência")
  • findBy* — assíncrona, retorna uma Promise (use para elementos que aparecem após trabalho assíncrono)

Interação do Usuário com userEvent

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

test("envia o formulário", async () => {
  const user = userEvent.setup();
  const onSubmit = vi.fn();
  render(<UserForm onSubmit={onSubmit} />);

  await user.type(screen.getByLabelText("Email"), "[email protected]");
  await user.click(screen.getByRole("button", { name: /salvar/i }));

  expect(onSubmit).toHaveBeenCalledWith({ email: "[email protected]" });
});
  • Sempre use await nas chamadas de userEvent
  • Chame userEvent.setup() uma vez por teste, reutilize o user retornado
  • userEvent simula uma sequência real de navegador; fireEvent despacha um único evento sintético — prefira userEvent

Padrões Assíncronos

// Elemento que aparece após trabalho assíncrono
expect(await screen.findByText("Carregado")).toBeInTheDocument();

// Afirmação de efeito colateral
await waitFor(() => expect(saveSpy).toHaveBeenCalled());

// Elemento que deve desaparecer
await waitForElementToBeRemoved(() => screen.queryByText("Carregando"));

Nunca use setTimeout + afirmação — é instável. Use os matchers acima.

Simulação de Rede com MSW

O Mock Service Worker simula na camada de rede. O componente, hooks e biblioteca de fetch se comportam exatamente como em produção.

Configuração

// test/setup.ts
import { setupServer } from "msw/node";
import { http, HttpResponse } from "msw";

export const handlers = [
  http.get("/api/users/:id", ({ params }) =>
    HttpResponse.json({ id: params.id, name: "Alice" }),
  ),
  http.post("/api/users", async ({ request }) => {
    const body = await request.json();
    return HttpResponse.json({ id: "new-id", ...body }, { status: 201 });
  }),
];

export const server = setupServer(...handlers);

beforeAll(() => server.listen({ onUnhandledRequest: "error" }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

Configure onUnhandledRequest: "error" para que qualquer requisição não simulada falhe o teste de forma evidente — aprovações silenciosas são piores que falhas.

Sobrescrita por teste

test("renderiza erro em 500", async () => {
  server.use(
    http.get("/api/users/:id", () => new HttpResponse(null, { status: 500 })),
  );
  render(<UserPage id="1" />);
  expect(await screen.findByText(/algo deu errado/i)).toBeInTheDocument();
});

Encapsulamento de Provedores

Encapsule os provedores uma vez em um test-utils.tsx:

// test-utils.tsx
import { render, RenderOptions } from "@testing-library/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

export function renderWithProviders(
  ui: React.ReactElement,
  options?: RenderOptions,
) {
  const queryClient = new QueryClient({
    defaultOptions: { queries: { retry: false } },
  });

  return render(
    <QueryClientProvider client={queryClient}>
      <ThemeProvider theme={lightTheme}>
        <MemoryRouter>{ui}</MemoryRouter>
      </ThemeProvider>
    </QueryClientProvider>,
    options,
  );
}

export * from "@testing-library/react";

Em seguida, use import { renderWithProviders, screen } from "test-utils" em cada arquivo de teste.

Teste de Hooks Personalizados

import { renderHook, act } from "@testing-library/react";

test("useCounter incrementa e decrementa", () => {
  const { result } = renderHook(() => useCounter(0));

  expect(result.current.count).toBe(0);

  act(() => result.current.increment());
  expect(result.current.count).toBe(1);

  act(() => result.current.decrement());
  expect(result.current.count).toBe(0);
});

test("useCounter aceita valor inicial", () => {
  const { result } = renderHook(() => useCounter(10));
  expect(result.current.count).toBe(10);
});

test("useUser busca dados do usuário", async () => {
  // Instancie o QueryClient UMA VEZ por teste fora do wrapper para que ele sobreviva a re-renderizações.
  // Criá-lo dentro do closure do wrapper redefine o cache a cada renderização, produzindo testes instáveis.
  const queryClient = new QueryClient({
    defaultOptions: { queries: { retry: false } },
  });
  const wrapper = ({ children }: { children: React.ReactNode }) => (
    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  );

  const { result } = renderHook(() => useUser("1"), { wrapper });

  await waitFor(() => expect(result.current.isSuccess).toBe(true));
  expect(result.current.data).toEqual({ id: "1", name: "Alice" });
});
  • Encapsule chamadas de mudança de estado em act
  • Teste apenas através da API pública do hook
  • Para hooks que usam contexto, passe um wrapper

Asserções de Acessibilidade

import { axe, toHaveNoViolations } from "jest-axe"; // ou vitest-axe
expect.extend(toHaveNoViolations);

test("UserCard não tem violações de a11y", async () => {
  const { container } = render(<UserCard user={mockUser} />);
  expect(await axe(container)).toHaveNoViolations();
});

Execute axe em testes de componente para cada componente interativo. Detecta:

  • Rótulos ausentes em inputs de formulário
  • Uso inválido de ARIA
  • Contraste de cor pobre (limitado — JSDOM não tem motor CSS real, então isso funciona apenas para estilos inline; contraste visual pertence ao Playwright)
  • Texto alternativo ausente em imagens
  • Violações de ordem de cabeçalhos

Link cruzado: skills/accessibility/SKILL.md para o guia mais amplo de testes de a11y.

Quando NÃO Usar Testes de Snapshot

Snapshots da saída renderizada:

  • Quebram a cada mudança de estilo
  • São aprovados mecanicamente durante a revisão
  • Testam detalhe de implementação (estrutura do DOM), não comportamento

Usos aceitáveis de snapshot:

  • Funções de serialização de dados pura (formatInvoice(invoice) -> string estável)
  • Arquivos de configuração gerados (ex.: saída de configuração do webpack)

Para regressão visual em componentes, use screenshots do Playwright/Cypress ou Percy/Chromatic — diferenças visuais reais, não strings do DOM.

Quando Usar Playwright / Cypress

O JSDOM (usado pelo Vitest/Jest) não pode:

  • Renderizar layout real (flexbox, grid, consultas de viewport)
  • Executar animações nativas do navegador, transições CSS
  • Testar comportamento de rolagem, arrastar e soltar, colar da área de transferência
  • Lidar com iframes, popups, downloads, fluxos de origem cruzada
  • Executar rede real em um ambiente controlado com suporte completo do DevTools

Para qualquer um desses casos, use Playwright Component Testing (teste de componente em navegador real) ou E2E completo. Veja e2e-testing skill.

Fronteira de decisão:

  • Um hook, um componente de apresentação, um formulário com lógica -> RTL
  • Um componente cujo layout importa ou que usa APIs de navegador não presentes no JSDOM -> Playwright CT
  • Um fluxo de usuário completo em várias páginas -> Playwright/Cypress E2E

Metas de Cobertura

CamadaMeta
Utilitários puros>=90%
Hooks personalizados>=85%
Componentes de apresentação>=80% — comportamento, não linhas
Componentes contêiner>=70% — caminhos felizes + estados de erro
PáginasE2E coberto separadamente; mínimo de teste de fumaça

Configure via vitest.config.ts / jest.config.js:

// vitest.config.ts
test: {
  coverage: {
    provider: "v8",
    reporter: ["text", "html", "lcov"],
    thresholds: {
      lines: 80,
      functions: 80,
      branches: 70,
      statements: 80,
    },
  },
}

Anti-Padrões

  • container.querySelector("...") — ignora consultas de acessibilidade, permite que testes passem quando usuários reais falhariam
  • Afirmar sobre o número de renderizações — detalhe de implementação
  • jest.mock("react", ...) — nunca simule o React. Refatore o componente
  • Simular componentes filhos por padrão — testa a integração, não o isolamento. Simule apenas quando o filho tem efeitos colaterais pesados
  • Ignorar avisos de act() — eles sinalizam bugs reais (atualização de estado após desmontagem, encapsulamento assíncrono ausente)
  • Compartilhar estado mutável entre testes — instabilidade quando a ordem dos testes muda
  • Testes que passam com it.skip() removido — seu teste não afirma realmente o que você pensa

Workflow TDD

VERMELHO -> Escreva um teste falhando para o próximo requisito
VERDE   -> Escreva o código mínimo do componente para passar
REFATORAR -> Melhore o componente, os testes permanecem verdes
REPETIR  -> Próximo requisito

Para novos componentes:

  1. Defina o tipo de prop e a assinatura do componente
  2. Escreva o primeiro teste para o caso mais simples
  3. Verifique se ele falha pelo motivo certo
  4. Implemente o suficiente para passar
  5. Adicione o próximo caso de teste
  6. Refatore quando o terceiro teste semelhante revelar um padrão

Comandos de Teste

# Vitest
vitest                            # modo watch
vitest run                        # execução única
vitest run --coverage             # com cobertura
vitest run path/to/file.test.tsx  # arquivo único

# Jest
jest --watch
jest --coverage
jest path/to/file.test.tsx

# Modo CI
CI=true vitest run --coverage

Relacionados

Exemplos

Envio de formulário com MSW e userEvent

test("envia formulário de usuário e mostra sucesso", async () => {
  server.use(
    http.post("/api/users", () =>
      HttpResponse.json({ id: "1", name: "Alice" }, { status: 201 }),
    ),
  );

  const user = userEvent.setup();
  renderWithProviders(<UserForm />);

  await user.type(screen.getByLabelText("Nome"), "Alice");
  await user.type(screen.getByLabelText("Email"), "[email protected]");
  await user.click(screen.getByRole("button", { name: /salvar/i }));

  expect(await screen.findByText(/salvo com sucesso/i)).toBeInTheDocument();
});

Testando um boundary de erro

function Broken() {
  throw new Error("boom");
}

test("boundary de erro renderiza fallback", () => {
  // Suprime o ruído de console.error do React para o erro esperado, depois restaura para
  // que o espião não vaze entre testes e esconda erros reais em outros lugares.
  const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
  try {
    render(
      <ErrorBoundary fallback={<div>Algo deu errado</div>}>
        <Broken />
      </ErrorBoundary>,
    );

    expect(screen.getByText("Algo deu errado")).toBeInTheDocument();
  } finally {
    errorSpy.mockRestore();
  }
});

Testando um boundary Suspense

test("mostra carregando e depois conteúdo", async () => {
  renderWithProviders(
    <Suspense fallback={<div>Carregando...</div>}>
      <UserDetail id="1" />
    </Suspense>,
  );

  expect(screen.getByText("Carregando...")).toBeInTheDocument();
  expect(await screen.findByText("Alice")).toBeInTheDocument();
});

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

Habilidades Relacionadas