¿Qué es Testing Library?
Testing Library es una familia de librerías que te ayudan a probar componentes de UI de la misma manera en que los experimenta un usuario. En lugar de acceder a los detalles internos de un componente, lo renderizas, buscas los elementos tal como lo haría una persona —por su rol, etiqueta o texto visible— e interactúas con ellos a través de eventos realistas.
La idea central es una reacción contra las pruebas basadas en detalles de implementación. Los enfoques antiguos renderizaban un componente y luego hacían aserciones sobre el estado interno, las props o una estructura específica del DOM. Esas pruebas se rompían en cada refactorización, aunque detectaban muy pocos errores reales. Testing Library cambia la perspectiva: si el usuario puede verlo y usarlo, puedes probarlo, y la prueba sobrevivirá a los cambios en el funcionamiento interno del componente.
Renderizado y consultas
Renderizas un componente y luego realizas consultas al DOM resultante a través de screen.
// LoginForm.test.jsx
import { render, screen } from "@testing-library/react";
import { LoginForm } from "./LoginForm";
test("shows the email field", () => {
render(<LoginForm />);
expect(screen.getByLabelText("Email")).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Sign in" })).toBeInTheDocument();
});
screen es la superficie de consulta recomendada. Busca en todo el documento renderizado, lo que refleja cómo un usuario ve la página en lugar de centrarse en un contenedor particular.
Eligiendo la query adecuada
Testing Library ofrece muchas queries, y el orden es importante. Prioriza aquellas que estén más cerca de la perspectiva del usuario:
- getByRole — botones, enlaces, encabezados, inputs y más, coincidiendo con el nombre accesible. Esta es la mejor opción por defecto.
- getByLabelText — controles de formulario encontrados a través de su etiqueta.
- getByPlaceholderText — cuando el placeholder es la única etiqueta disponible.
- getByText — contenido de texto no interactivo.
- getByDisplayValue — el valor actual de un elemento de formulario.
- getByAltText — imágenes mediante su texto alternativo.
- getByTitle y getByTestId — como último recurso.
// queries.jsx
screen.getByRole("heading", { level: 1, name: "Dashboard" });
screen.getByLabelText("Password");
screen.getByText("Welcome back");
screen.getByAltText("Company logo");
screen.getByTestId("chart");
getByRole es potente porque también sirve como una comprobación de accesibilidad. Si un control no tiene un nombre accesible, la query falla, lo que significa que es probable que tu markup también sea inaccesible.
Cada query tiene variantes: getBy lanza un error si nada coincide, queryBy devuelve null, y findBy devuelve una promesa que espera. Usa queryBy para asegurar que algo está ausente, y findBy después de una actualización asíncrona.
Interactuando con userEvent
userEvent simula la secuencia completa de eventos que genera un usuario real.
// Search.test.jsx
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { Search } from "./Search";
test("submits the query", async () => {
const onSearch = vi.fn();
render(<Search onSearch={onSearch} />);
await userEvent.type(screen.getByLabelText("Search"), "vitest");
await userEvent.click(screen.getByRole("button", { name: "Go" }));
expect(onSearch).toHaveBeenCalledWith("vitest");
});
userEvent gestiona los eventos de foco, teclado y puntero de forma conjunta, lo que permite detectar errores que un único fireEvent.change pasaría por alto. Prefiérelo para todo, excepto en los casos excepcionales en los que no cubra la funcionalidad necesaria.
Comportamiento asíncrono
La mayoría de los componentes se actualizan de forma asíncrona, ya sea a través de un fetch, un temporizador o una transición. Utiliza findBy y waitFor en lugar de retardos arbitrarios.
// Users.test.jsx
test("renders users after loading", async () => {
render(<Users />);
expect(screen.getByText("Loading…")).toBeInTheDocument();
const items = await screen.findAllByRole("listitem");
expect(items).toHaveLength(3);
});
findBy espera a que aparezca un elemento coincidente y falla con un mensaje útil si nunca aparece. waitFor se utiliza para esperar una aserción o una condición que no sea una simple búsqueda de elementos. Nunca utilices timeouts reales; hacen que las pruebas sean lentas e inestables.
Asserts con jest-dom
El paquete complementario @testing-library/jest-dom añade matchers que se leen de forma natural para el estado del DOM.
// matchers.test.jsx
expect(button).toBeDisabled();
expect(input).toHaveValue("[email protected]");
expect(dialog).not.toBeInTheDocument();
expect(heading).toHaveTextContent("Dashboard");
expect(link).toHaveAttribute("href", "/docs");
Estos matchers hacen que la intención sea más clara que las comprobaciones directas de las propiedades del DOM y generan mejores mensajes de error.
Pruebas priorizando la accesibilidad
Dado que getByRole se basa en roles y nombres accesibles, escribir las pruebas de esta manera permite detectar problemas de accesibilidad desde el principio. Si no puedes encontrar un botón por su rol, los usuarios de lectores de pantalla tampoco podrán encontrarlo. Añadir getByRole y toHaveAccessibleName a tus pruebas es una de las formas más económicas de mejorar la accesibilidad y, a menudo, sustituye la necesidad de realizar una auditoría automatizada independiente.
Testing Library en diferentes frameworks
La librería cuenta con adaptadores para React, Vue, Svelte, Angular y otros. La API de consultas es compartida, por lo que el modelo mental se mantiene aunque la configuración varíe. El adaptador de React es el más utilizado y el que se muestra aquí.
Mejores prácticas
- Realiza las consultas primero por rol, luego por etiqueta y después por texto; utiliza los test ids solo como último recurso.
- Usa
userEventen lugar defireEvent. - Espera a
findByywaitForen vez de utilizar retardos (delays). - Realiza aserciones sobre lo que el usuario ve, no sobre el estado interno.
- Mantén los tests enfocados en un solo comportamiento cada uno.
- Limpia automáticamente entre tests; Testing Library hace esto por defecto.
- Trata un
getByRolefallido como una pista sobre un problema de accesibilidad.
Errores comunes
- Recurrir a
container.querySelectory testear nombres de clases. - Usar
fireEventcuandouserEventdetectaría más casos. - Usar esperas arbitrarias con
setTimeouten lugar defindBy. - Hacer snapshots de todo el árbol renderizado.
- Hacer aserciones sobre el estado o las props del componente en lugar del DOM.
- Añadir test ids a todo y perder los beneficios de la accesibilidad.
Próximos pasos
Testing Library es la capa de componentes de una estrategia de pruebas sólida. Ejecútala con Vitest o Jest, profundiza en tus conocimientos de React para que tus componentes sean testeables y cubre flujos completos con Playwright. Después, toma un componente que ya hayas construido y escribe una prueba desde la perspectiva del usuario.