Naki-404/testes-frontend

Testes unitários e de componentes frontend com Vitest e React Testing Library: testes focados em comportamento, consultas acessíveis, user-event, simulação de rede com MSW, hooks, UI assíncrona e cobertura. Use ao escrever ou corrigir testes para componentes React, hooks ou páginas, ou quando os testes estiverem instáveis, lentos ou acoplados a detalhes de implementação.

O que é testes-frontend?

Esta skill define as melhores práticas para escrever testes de frontend focados em comportamento utilizando Vitest e React Testing Library, com forte ênfase em acessibilidade, simulação de rede com MSW e interações realistas do usuário com `userEvent`. Ela é ideal para desenvolvedores que desejam criar testes robustos, de fácil manutenção e que evitem o acoplamento a detalhes internos de implementação. A skill também fornece um fluxo de trabalho claro, um guia de consultas acessíveis e dicas para lidar com hooks, temporizadores e armadilhas comuns. Funciona em qualquer projeto React configurado com Vitest e React Testing Library, e é complementar a outras skills como `naki-tdd` e `naki-e2e-playwright`.

Funciona com~Claude Code~Codex CLI~Cursor
Part ofReact
npx skills add https://github.com/Naki-404/naki-claude-kit/tree/HEAD/kit/skills/naki-frontend-testing

Installed? Explore more Programação e Desenvolvimento skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Perguntar na sua IA favorita

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

Documentação

Testes frontend

Teste o que o usuário vê e faz, não como o componente é construído. Combine com naki-tdd para o ciclo, naki-e2e-playwright para fluxos completos no navegador e naki-accessibility para semântica. Siga a skill de regras do projeto se houver uma (por exemplo naki-myapp).

Quando usar

  • Um novo componente, hook ou página precisa de testes.
  • Um teste quebra após uma refatoração que não alterou o comportamento (sinal de acoplamento de implementação).
  • Testes estão instáveis, usam waitFor em todo lugar ou simulam metade do aplicativo.

Regras principais

  1. Faça consultas como um usuário. Prioridade: getByRole (com name), getByLabelText, getByPlaceholderText, getByText, getByDisplayValue. Use getByTestId apenas quando nada acessível existir.
  2. Interaja com userEvent, não com fireEvent. Crie-o com const user = userEvent.setup() e use await em toda chamada.
  3. Afirme sobre a saída: texto visível, funções (roles), valores de formulário, chamadas a um endpoint de rede. Nunca afirme sobre variáveis de estado, detalhes internos de hooks, nomes de classes CSS ou métodos de instância do componente.
  4. Simule a rede, não seus módulos. Use handlers do MSW na camada HTTP. Simule um módulo apenas para limites verdadeiramente externos (tempo, aleatoriedade, APIs do navegador não presentes no jsdom).
  5. Use findBy* para coisas que aparecem depois. Reserve waitFor para asserções que não são uma consulta, e nunca coloque efeitos colaterais dentro de waitFor.
  6. Um comportamento por teste, nomeado como uma frase: mostra um erro quando o servidor retorna 500.
  7. Cada teste configura seu próprio estado. Sem dependência de ordem; redefina handlers, temporizadores e armazenamento em afterEach.
  8. Forneça providers reais através de um helper renderWithProviders (roteador, query client com retries desativados, tema) em vez de simulá-los.
  9. Acessibilidade é testável. Uma falha em getByRole("button", { name: "Salvar" }) geralmente significa um bug de acessibilidade real. Opcionalmente, adicione verificações vitest-axe para telas principais.
  10. Cobertura é um sinal, não uma meta. Cubra ramos que importam: estados de erro, estados vazios, permissões.

Folha de consulta

ElementoConsulta
botãogetByRole("button", { name: /salvar/i })
linkgetByRole("link", { name: "Docs" })
entrada de texto com rótulogetByRole("textbox", { name: "Email" }) ou getByLabelText("Email")
checkboxgetByRole("checkbox", { name: "Lembrar de mim" })
títulogetByRole("heading", { level: 2, name: "Alertas" })
linha de tabelagetAllByRole("row") then within(row)
diálogogetByRole("dialog", { name: "Excluir alerta" })
ausênciaexpect(queryByText("Erro")).not.toBeInTheDocument()

Fluxo de trabalho

  1. Liste os comportamentos: estados de renderização (carregando, vazio, dados, erro) e interações.
  2. Adicione handlers MSW para os endpoints que o componente chama; padrão para sucesso.
  3. Escreva um teste por comportamento, sobrescrevendo handlers com server.use(...) para casos de erro.
  4. Execute npx vitest run caminho/para/arquivo.test.tsx, depois toda a suíte.
  5. Quebre a implementação de propósito uma vez (comente uma linha) para confirmar que o teste pode falhar.

Configuração

// vitest.setup.ts
import "@testing-library/jest-dom/vitest";
import { afterAll, afterEach, beforeAll } from "vitest";
import { cleanup } from "@testing-library/react";
import { server } from "./src/test/server";

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

onUnhandledRequest: "error" faz com que um handler ausente falhe de forma ruidosa em vez de travar.

Exemplo

import { http, HttpResponse } from "msw";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { server } from "../test/server";
import { AlertList } from "./AlertList";

test("lista alertas e filtra por gravidade", async () => {
  server.use(http.get("/api/alerts", () => HttpResponse.json([
    { id: "a1", title: "Força bruta na vpn", severity: "high" },
    { id: "a2", title: "Novo login admin", severity: "low" },
  ])));
  const user = userEvent.setup();
  render(<AlertList />);

  expect(await screen.findByText("Força bruta na vpn")).toBeInTheDocument();
  await user.selectOptions(screen.getByRole("combobox", { name: "Gravidade" }), "high");
  expect(screen.queryByText("Novo login admin")).not.toBeInTheDocument();
});

test("mostra um botão de repetir quando o carregamento falha", async () => {
  server.use(http.get("/api/alerts", () => new HttpResponse(null, { status: 500 })));
  render(<AlertList />);
  expect(await screen.findByRole("button", { name: /repetir/i })).toBeInTheDocument();
});

Hooks e tempo

  • Teste um hook através de um componente pequeno que o usa, ou com renderHook quando ele não tem IU.
  • Temporizadores falsos: vi.useFakeTimers() e userEvent.setup({ advanceTimers: vi.advanceTimersByTime }); restaure com vi.useRealTimers() em afterEach.
  • Congele datas com vi.setSystemTime(new Date("2026-01-01T00:00:00Z")) para que snapshots e rótulos sejam estáveis.

Lista de verificação

  • Consultas são baseadas em função (role) ou rótulo.
  • Todas as chamadas userEvent com await.
  • Rede simulada com MSW; requisições não tratadas falham.
  • Estados de carregamento, erro e vazio cobertos.
  • Nenhuma asserção sobre detalhes internos ou nomes de classe.
  • Testes passam sozinhos e em qualquer ordem.
  • Nenhum aviso act(...) na saída.

Erros comuns

ErroCorreção
getByTestId em todo lugarconsultas por função/rótulo; adicione rótulos ao componente se estiver faltando
await waitFor(() => getByText("x"))await findByText("x")
fireEvent.change para digitarawait user.type(input, "texto")
Simular o hook useAlerts diretamentesimule o endpoint HTTP
Snapshots grandes de páginas inteirasasserções explícitas sobre o que importa
Query client com retries ativados, testes de erro lentosretry: false no provider de teste
Avisos act ignoradosaguarde a atualização da UI assíncrona com findBy*

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

Habilidades Relacionadas