Testing de Componentes
React Testing Library (RTL) se basa en una filosofía que la distingue de otras herramientas de testing: escribe tests que se comporten como un usuario real, no como un desarrollador que conoce el código interno.
En lugar de verificar si el estado tiene cierto valor o si una función fue llamada X veces, verificas lo que el usuario vería y haría: "¿aparece el texto en pantalla? ¿al hacer clic en el botón cambia lo que se muestra? ¿el formulario muestra el error correcto?" Esto hace los tests resistentes a refactorizaciones.
RTL tiene queries en orden de preferencia según accesibilidad: getByRole (busca por roles ARIA como "button", "textbox", "heading") es el más recomendado porque refleja cómo los lectores de pantalla ven la app. Luego getByLabelText para inputs asociados a labels, getByPlaceholderText, y getByText.
Las variantes: getBy lanza error si no encuentra (para verificar que algo existe), queryBy devuelve null si no encuentra (para verificar que algo no está), y findBy es asíncrono con await (para elementos que aparecen después de una petición a la API o un timer).
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { Contador } from "./Contador";
// Test básico
test("muestra el contador inicial en 0", () => {
render(<Contador />);
expect(screen.getByText("Conteo: 0")).toBeInTheDocument();
});
// Test de interacción
test("incrementa al hacer clic en +", async () => {
const user = userEvent.setup();
render(<Contador />);
await user.click(screen.getByRole("button", { name: "+" }));
expect(screen.getByText("Conteo: 1")).toBeInTheDocument();
await user.click(screen.getByRole("button", { name: "+" }));
expect(screen.getByText("Conteo: 2")).toBeInTheDocument();
});
// Test de formulario
test("muestra error si el email es inválido", async () => {
const user = userEvent.setup();
render(<FormRegistro />);
await user.type(screen.getByPlaceholderText("Email"), "invalido");
await user.click(screen.getByRole("button", { name: "Registrarse" }));
expect(screen.getByText("Email inválido")).toBeInTheDocument();
});
// Test async (fetch)
test("carga y muestra usuarios", async () => {
// Mock de fetch
global.fetch = jest.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve([{ id: 1, name: "Ana" }])
});
render(<ListaUsuarios />);
expect(screen.getByText("Cargando...")).toBeInTheDocument();
await waitFor(() => {
expect(screen.getByText("Ana")).toBeInTheDocument();
});
});
// Snapshot test
test("coincide con el snapshot", () => {
const { container } = render(<Boton texto="Guardar" />);
expect(container).toMatchSnapshot();
});
Practica esta lección
Resuelve los ejercicios del curso de React en la app, gratis y con corrección automática.
Empezar a practicar