Busca en todas las páginas de la documentación
Prueba el renderizado de componentes, las interacciones del usuario, la UI condicional y el contenido dinámico con confianza.
Tarjeta de referencia rápida — lista para copiar y pegar.
import { render, screen, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
// Test rendering with props
render(<Alert severity="warning" message="Disk full" />);
expect(screen.getByRole("alert")).toHaveTextContent("Disk full");
// Test user interactions
const user = userEvent.setup();
await user.click(screen.getByRole("button", { name: /delete/i }));
expect(screen.queryByText("Item 1")).not.toBeInTheDocument();
// Test conditional rendering
render(<Banner show={false} />);
expect(screen.queryByRole("banner")).not.toBeInTheDocument();
// Test lists
const items = screen.getAllByRole("listitem");
expect(items).toHaveLength(3);
expect(within(items[0]).getByText("First")).toBeInTheDocument();Cuándo usarlo: Siempre que construyas un componente que renderice props, maneje interacciones o muestre UI condicional.
// src/components/todo-list.tsx
"use client";
import { useState } from "react";
interface Todo {
id: string;
text: string;
completed: boolean;
}
export function TodoList
// src/components/todo-list.test.tsx
import { render, screen, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { vi, describe, it, expect, beforeEach } from "vitest";
import { TodoList } from "./todo-list";
// Mock crypto.randomUUID for deterministic IDs
beforeEach(() => {
Lo que demuestra esto:
queryBywithin()aria-label accesible para consultas dirigidas al botón de eliminarrender() crea un árbol DOM nuevo — las pruebas están aisladas por defectowithin() limita las consultas a un elemento contenedor específico, útil para probar elementos individuales de una listaqueryBy devuelve null en lugar de lanzar un error, lo que lo hace ideal para afirmar ausenciagetAllBy devuelve un array de elementos coincidentes — usa .toHaveLength() para afirmar el tamaño de listasPruebas con snapshot (úsalas con moderación):
it("matches snapshot", () => {
const { container } = render(
<Alert severity="error" message="Something broke" />
);
expect(container.firstChild).toMatchSnapshot();
});
// Snapshots are useful for detecting unintended UI changes
Snapshots inline para salida pequeña:
it("renders correct class names", () => {
render(<Badge variant="success">OK</Badge>);
expect(screen.getByText("OK").className).toMatchInlineSnapshot(
`"badge badge-success"`
);
});Pruebas con rerender para cambios de props:
it("updates when severity changes", () => {
const { rerender } = render(<Alert severity="info" message="Note" />);
expect(screen.getByRole("alert")).toHaveClass("alert-info");
// Type-safe test factories for complex props
function createTodo(overrides: Partial<Todo> = {}): Todo {
return {
id: crypto.randomUUID(),
text: "Default todo",
completed: false,
...overrides,
};
Probar detalles de implementación — Comprobar state interno, métodos de instancia o nombres de clase CSS ata las pruebas a la estructura del código. Solución: Afirma lo que ve el usuario — contenido de texto, visibilidad, estado habilitado/deshabilitado.
Depender demasiado de snapshots — Los snapshots grandes son difíciles de revisar y se aprueban sin mirar al actualizarse. Solución: Usa snapshots solo para salida pequeña y estable. Prefiere afirmaciones explícitas.
Olvidar esperar userEvent — Todos los métodos de userEvent devuelven promesas en v14+. Solución: Siempre haz await o la prueba puede pasar antes de que la interacción termine.
No usar within() para elementos de lista — screen.getByText("Eliminar") coincide con el primer botón de eliminar de la página. Solución: Limita el alcance al elemento de lista específico con within() o usa atributos aria-label únicos.
Probar demasiadas cosas en una sola prueba — Las pruebas que añaden, alternan y eliminan en un solo bloque it son difíciles de depurar cuando fallan. Solución: Escribe pruebas enfocadas para cada comportamiento.
| Alternativa | Úsala cuando | No la uses cuando |
|---|---|---|
| Storybook + Chromatic | Necesitas pruebas de regresión visual junto a las stories de componentes | Solo necesitas pruebas de comportamiento |
| Playwright Component Testing | Necesitas renderizado en un navegador real (canvas, CSS complejo) | Las pruebas unitarias rápidas de componentes son suficientes |
| Solo pruebas con snapshot | Proteger contra cambios no deseados en componentes pequeños y estables | Los componentes cambian con frecuencia o los snapshots son grandes |
Usa queryBy, que devuelve null en lugar de lanzar un error:
render(<Banner show={false} />);
expect(screen.queryByRole("banner")).not.toBeInTheDocument();Usa within() para limitar las consultas a un contenedor:
const items = screen.getAllByRole("listitem");
expect(within(items[0]).getByText("First")).toBeInTheDocument();toHaveTextContent o toBeInTheDocument.Usa rerender del resultado de render:
const { rerender } = render(<Alert severity="info" message="Note" />);
rerender(<Alert severity="error" message="Note" />);
expect(screen.getByRole(screen.getByText("Eliminar") coincide con el botón equivocado?Si varios elementos contienen el mismo texto, getByText coincide con el primero. Usa within() para limitar el alcance a un padre específico, o usa atributos aria-label únicos.
crypto.randomUUID() para IDs de prueba deterministas?let counter = 0;
vi.stubGlobal("crypto", {
randomUUID: () => `test-id-${++counter}`,
});it?await en los métodos de userEvent?En userEvent v14+, todos los métodos son asíncronos. Sin await, las interacciones pueden no completarse antes de que se ejecuten las afirmaciones, provocando falsos positivos o pruebas inestables.
function createTodo(overrides: Partial<Todo> = {}): Todo {
return {
id: crypto.randomUUID(),
text: "Default todo",
completed: false,
...overrides,
};
Sí. La versión moderna de Testing Library limpia automáticamente después de cada prueba desmontando componentes y eliminando nodos del DOM. No necesitas llamar a cleanup() manualmente.
const items = screen.getAllByRole("listitem");
expect(items).toHaveLength(3);.snap separado.