Inicio > Cursos > React > Testing de Componentes
ReactReact AvanzadoLección 18 de 21

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();
});
Consejo🧪 Queries de Testing Library en orden de preferencia: getByRole > getByLabelText > getByPlaceholderText > getByText. Los roles semánticos son más robustos.
← Error Boundaries y SuspenseEstado Global: Zustand y Redux Toolkit →

Practica esta lección

Resuelve los ejercicios del curso de React en la app, gratis y con corrección automática.

Empezar a practicar