Communitygithub.com

react-testing

Pruebas de componentes React con React Testing Library, Vitest/Jest, MSW para simulación de red, aserciones de accesibilidad con axe y el límite de decisión entre pruebas de componentes y pruebas de extremo a extremo con Playwright/Cypress. Úsalo al escribir o corregir pruebas para componentes, hooks o páginas de React.

¿Qué es react-testing?

Esta habilidad proporciona una guía completa y práctica para escribir pruebas de React centradas en el comportamiento del usuario, utilizando el ecosistema moderno de herramientas como React Testing Library, Vitest/Jest, Mock Service Worker (MSW) para simular peticiones HTTP, y axe para auditorías de accesibilidad. El objetivo principal es enseñar a los desarrolladores a probar lo que el usuario ve y hace, evitando detalles de implementación como el estado interno del componente, props pasados a hijos o la estructura del DOM. La habilidad cubre desde la configuración inicial del proyecto (eligiendo entre Vitest y Jest) hasta patrones avanzados como el testing de hooks personalizados, el manejo de estados asíncronos con `findBy` y `waitFor`, la creación de wrappers de proveedores (React Query, Router, Temas), y la simulación de errores de red. Además, establece claramente cuándo es apropiado usar React Testing Library (RTL) versus herramientas de pruebas en navegador real como Playwright Component Testing o Cypress, ofreciendo criterios de decisión basados en las capacidades de JSDOM. Incluye objetivos de cobertura por capa (utilidades, hooks, componentes, páginas), anti-patrones comunes a evitar (como `container.querySelector`, mocks excesivos o ignorar warnings de `act`), y un flujo de trabajo TDD recomendado. También proporciona ejemplos completos de pruebas de formularios con MSW, pruebas de límites de error (ErrorBoundary) y Suspense, y comandos de ejecución para Vitest y Jest en diferentes modos (watch, CI, cobertura).

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

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Pruebas en React

Patrones completos de pruebas en React para pruebas de componentes centradas en el comportamiento, pruebas de hooks personalizados, aserciones de accesibilidad y simulación de red a nivel de red.

Cuándo activarlo

  • Escribir pruebas para componentes, hooks personalizados o páginas de React
  • Añadir cobertura de pruebas a componentes antiguos sin probar
  • Migrar desde Enzyme o patrones de la era de componentes de clase a React Testing Library
  • Configurar Vitest o Jest para un nuevo proyecto React
  • Simular peticiones HTTP en pruebas
  • Verificar violaciones de accesibilidad
  • Decidir qué pruebas pertenecen a RTL vs Playwright Component Testing vs E2E completo

Principio fundamental

Prueba lo que el usuario ve y hace, no los detalles de implementación.

Una prueba debe:

  • Renderizar el componente con los mismos proveedores que tiene en producción
  • Interactuar con él mediante consultas accesibles (role, label) y userEvent
  • Verificar la salida visible y los efectos secundarios observables (callback ejecutado, petición enviada)

Una prueba NO debe:

  • Inspeccionar el estado del componente, las props pasadas a hijos, ni qué hooks se llamaron
  • Simular el propio React o hooks del framework
  • Verificar el número de renderizados o la estructura del DOM más allá de lo que afecta a los usuarios

Elección de la librería

EjecutorCuándoNota
VitestVite, Remix, configuraciones modernasMás rápido, ESM nativo, API compatible con Jest
JestNext.js, CRA, repositorios establecidosPredeterminado para muchos proyectos React
Playwright Component TestingSe necesita un motor de navegador realUsar cuando a JSDOM le falte la característica requerida
Cypress Component TestingNavegador real, Cypress ya en usoAlternativa a Playwright CT

Elige uno. No ejecutes RTL + Vitest Y Playwright CT en el mismo repositorio a menos que tengas una separación de carriles clara.

Prioridad de consultas

React Testing Library expone consultas en tres niveles: úsalas de arriba abajo:

  1. Accesible para todos: getByRole, getByLabelText, getByPlaceholderText, getByText, getByDisplayValue
  2. Semántico: getByAltText, getByTitle
  3. IDs de prueba (escape): getByTestId
// Mejor
screen.getByRole("button", { name: /guardar/i });

// Aceptable para inputs
screen.getByLabelText("Correo electrónico");

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

Variantes:

  • getBy* — lanza una excepción si no hay coincidencia
  • queryBy* — devuelve null (útil para "verificar ausencia")
  • findBy* — asíncrono, devuelve una Promesa (útil para elementos que aparecen después de trabajo asíncrono)

Interacción del usuario con userEvent

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

test("envía el formulario", async () => {
  const user = userEvent.setup();
  const onSubmit = vi.fn();
  render(<FormularioUsuario onSubmit={onSubmit} />);

  await user.type(screen.getByLabelText("Correo electrónico"), "[email protected]");
  await user.click(screen.getByRole("button", { name: /guardar/i }));

  expect(onSubmit).toHaveBeenCalledWith({ correo: "[email protected]" });
});
  • Siempre usa await en las llamadas a userEvent
  • Llama a userEvent.setup() una vez por prueba, reutiliza el user devuelto
  • userEvent simula una secuencia real del navegador; fireEvent despacha un único evento sintético — prefiere userEvent

Patrones asíncronos

// Elemento que aparece después de trabajo asíncrono
expect(await screen.findByText("Cargado")).toBeInTheDocument();

// Verificación de efecto secundario
await waitFor(() => expect(guardarSpy).toHaveBeenCalled());

// Elemento que debe desaparecer
await waitForElementToBeRemoved(() => screen.queryByText("Cargando..."));

Nunca uses setTimeout + verificación — es inestable. Usa los matchers anteriores.

Simulación de red con MSW

Mock Service Worker simula a nivel de red. El componente, los hooks y la librería fetch se comportan exactamente como en producción.

Configuración

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

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

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

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

Configura onUnhandledRequest: "error" para que cualquier petición no simulada falle la prueba de forma ruidosa — las aprobaciones silenciosas son peores que las fallas.

Anulación por prueba

test("muestra error en 500", async () => {
  server.use(
    http.get("/api/usuarios/:id", () => new HttpResponse(null, { status: 500 })),
  );
  render(<PaginaUsuario id="1" />);
  expect(await screen.findByText(/algo salió mal/i)).toBeInTheDocument();
});

Envoltorio de proveedores

Envuelve los proveedores una vez en un test-utils.tsx:

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

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

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

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

Luego importa import { renderConProveedores, screen } from "test-utils" en cada archivo de prueba.

Pruebas de hooks personalizados

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

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

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

  act(() => result.current.incrementar());
  expect(result.current.cuenta).toBe(1);

  act(() => result.current.decrementar());
  expect(result.current.cuenta).toBe(0);
});

test("useContador acepta valor inicial", () => {
  const { result } = renderHook(() => useContador(10));
  expect(result.current.cuenta).toBe(10);
});

test("useUsuario obtiene datos del usuario", async () => {
  // Instancia QueryClient UNA VEZ por prueba fuera del wrapper para que sobreviva a re-renderizados.
  // Crearlo dentro del closure del wrapper reinicia el estado de caché en cada renderizado, produciendo pruebas inestables.
  const queryClient = new QueryClient({
    defaultOptions: { queries: { retry: false } },
  });
  const wrapper = ({ children }: { children: React.ReactNode }) => (
    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  );

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

  await waitFor(() => expect(result.current.isSuccess).toBe(true));
  expect(result.current.data).toEqual({ id: "1", nombre: "Alicia" });
});
  • Envuelve las llamadas que cambian el estado en act
  • Prueba a través de la API pública del hook solamente
  • Para hooks que usan contexto, pasa un wrapper

Aserciones de accesibilidad

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

test("TarjetaUsuario no tiene violaciones de accesibilidad", async () => {
  const { container } = render(<TarjetaUsuario usuario={usuarioSimulado} />);
  expect(await axe(container)).toHaveNoViolations();
});

Ejecuta axe en pruebas de componentes para cada componente interactivo. Detecta:

  • Etiquetas faltantes en inputs de formularios
  • Uso inválido de ARIA
  • Contraste de color pobre (limitado — JSDOM no tiene un motor CSS real, por lo que esto funciona solo para estilos en línea; el contraste visual pertenece a Playwright)
  • Texto alternativo faltante en imágenes
  • Violaciones de orden de encabezados

Enlace cruzado: skills/accessibility/SKILL.md para el manual más amplio de pruebas de accesibilidad.

Cuándo NO usar pruebas de instantáneas

Las instantáneas de la salida renderizada:

  • Se rompen con cada cambio de estilo
  • Se aprueban mecánicamente durante la revisión
  • Prueban detalle de implementación (estructura del DOM), no comportamiento

Usos aceptables de instantáneas:

  • Funciones puras de serialización de datos (formatearFactura(factura) -> cadena estable)
  • Archivos de configuración generados (ej., salida de configuración de webpack)

Para regresión visual en componentes, usa capturas de pantalla de Playwright/Cypress o Percy/Chromatic — diferencias visuales reales, no cadenas de DOM.

Cuándo usar Playwright / Cypress

JSDOM (usado por Vitest/Jest) no puede:

  • Renderizar diseño real (flexbox, grid, consultas de viewport)
  • Ejecutar animaciones nativas del navegador, transiciones CSS
  • Probar comportamiento de scroll, arrastrar y soltar, pegar desde portapapeles
  • Manejar iframes, ventanas emergentes, descargas, flujos de origen cruzado
  • Ejecutar red real en un entorno controlado con soporte completo de DevTools

Para cualquiera de esos, usa Playwright Component Testing (prueba de componente en navegador real) o E2E completo. Consulta e2e-testing skill.

Límite de decisión:

  • Un hook, un componente de presentación, un formulario con lógica -> RTL
  • Un componente cuyo diseño importa o que usa APIs del navegador no presentes en JSDOM -> Playwright CT
  • Un flujo de usuario completo a través de múltiples páginas -> Playwright/Cypress E2E

Objetivos de cobertura

CapaObjetivo
Utilidades puras>=90%
Hooks personalizados>=85%
Componentes de presentación>=80% — comportamiento, no líneas
Componentes contenedores>=70% — caminos felices + estados de error
PáginasE2E cubiertas por separado; mínimo prueba de humo

Configura mediante 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-patrones

  • container.querySelector("...") — evita consultas de accesibilidad, permite que las pruebas pasen cuando los usuarios reales fallarían
  • Verificar el número de renderizados — detalle de implementación
  • jest.mock("react", ...) — nunca simules React. Refactoriza el componente en su lugar
  • Simular componentes hijos por defecto — prueba la integración, no el aislamiento. Simula solo cuando el hijo tiene efectos secundarios pesados
  • Ignorar advertencias de act() — señalan errores reales (actualización de estado después de desmontar, envoltura asíncrona faltante)
  • Compartir estado mutable entre pruebas — falla cuando el orden de las pruebas cambia
  • Pruebas que pasan con it.skip() eliminado — tu prueba no verifica realmente lo que crees

Flujo de trabajo TDD

ROJO     -> Escribir prueba fallida para el siguiente requisito
VERDE    -> Escribir código mínimo del componente para que pase
REFACTORIZAR -> Mejorar el componente, las pruebas permanecen en verde
REPETIR  -> Siguiente requisito

Para componentes nuevos:

  1. Define el tipo de prop y la firma del componente
  2. Escribe la primera prueba para el caso más simple
  3. Verifica que falla por la razón correcta
  4. Implementa lo suficiente para que pase
  5. Añade el siguiente caso de prueba
  6. Refactoriza cuando la tercera prueba similar revele un patrón

Comandos de prueba

# Vitest
vitest                            # modo observador
vitest run                        # ejecución única
vitest run --coverage             # con cobertura
vitest run ruta/al/archivo.test.tsx  # archivo único

# Jest
jest --watch
jest --coverage
jest ruta/al/archivo.test.tsx

# Modo CI
CI=true vitest run --coverage

Relacionado

Ejemplos

Envío de formulario con MSW y userEvent

test("envía formulario de usuario y muestra éxito", async () => {
  server.use(
    http.post("/api/usuarios", () =>
      HttpResponse.json({ id: "1", nombre: "Alicia" }, { status: 201 }),
    ),
  );

  const user = userEvent.setup();
  renderConProveedores(<FormularioUsuario />);

  await user.type(screen.getByLabelText("Nombre"), "Alicia");
  await user.type(screen.getByLabelText("Correo electrónico"), "[email protected]");
  await user.click(screen.getByRole("button", { name: /guardar/i }));

  expect(await screen.findByText(/guardado exitosamente/i)).toBeInTheDocument();
});

Probar un límite de error

function Roto() {
  throw new Error("explosión");
}

test("el límite de error renderiza la alternativa", () => {
  // Suprimir el ruido de console.error de React para el error esperado, luego restaurar para que
  // el espía no se filtre entre pruebas y oculte errores reales en otros lugares.
  const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
  try {
    render(
      <LimiteError fallback={<div>Algo salió mal</div>}>
        <Roto />
      </LimiteError>,
    );

    expect(screen.getByText("Algo salió mal")).toBeInTheDocument();
  } finally {
    errorSpy.mockRestore();
  }
});

Probar un límite de Suspense

test("muestra carga y luego contenido", async () => {
  renderConProveedores(
    <Suspense fallback={<div>Cargando...</div>}>
      <DetalleUsuario id="1" />
    </Suspense>,
  );

  expect(screen.getByText("Cargando...")).toBeInTheDocument();
  expect(await screen.findByText("Alicia")).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.

Skills relacionados