Compartir en Twitter
Go to Homepage

APRENDE A TESTEAR APLICACIONES REACT CON TESTING LIBRARY 2025

November 2, 2025

Introducción a las Pruebas en React con Testing Library

En el ecosistema actual del desarrollo frontend, donde React sigue dominando como una de las bibliotecas más utilizadas para construir interfaces de usuario, la calidad del código se ha convertido en un factor determinante para el éxito de cualquier proyecto. Las pruebas automatizadas no solo garantizan que las funcionalidades existentes sigan operando correctamente tras modificaciones, sino que también proporcionan una red de seguridad que permite a los equipos evolucionar sus aplicaciones con confianza.

React Testing Library emerge como la herramienta preferida en 2025 para escribir pruebas que realmente reflejan cómo los usuarios interactan con las aplicaciones. A diferencia de enfoques tradicionales que se centran en detalles de implementación interna, esta biblioteca promueve un paradigma donde las pruebas simulan usuarios reales interactuando con elementos del DOM renderizados en el navegador.

Este enfoque filosófico no es casual. Al priorizar la experiencia del usuario final sobre la estructura interna de los componentes, se logra una mayor resiliencia ante cambios de refactorización. Un componente puede ser completamente reescrito utilizando diferentes hooks o patron, pero mientras mantenga el mismo comportamiento visible en la interfaz, las pruebas continuarán pasando.

Configuración Inicial del Entorno de Pruebas

La integración de React Testing Library en proyectos modernos se ha simplificado significativamente gracias a las herramientas de scaffolding actuales. Cuando se inicializa una aplicación React mediante Create React App o Vite, ambas incluyen por defecto Jest como motor de pruebas y React Testing Library como conjunto de utilidades para interactuar con el DOM.

Para proyectos que requieren configuración manual, la instalación se realiza mediante npm o yarn:

npm install --save-dev @testing-library/react @testing-library/jest-dom jest

Es importante destacar que React Testing Library no es un framework de pruebas completo, sino una biblioteca de utilidades. Jest proporciona el runner de pruebas, las aserciones y el entorno de ejecución, mientras que Testing Library ofrece los métodos para consultar y manipular el DOM virtual.

La configuración mínima requiere la importación de los matchers personalizados de jest-dom, que extienden las capacidades de aserción:

// src/setupTests.js
import "@testing-library/jest-dom";

Este archivo se ejecuta automáticamente antes de cada suite de pruebas cuando se utiliza Create React App.

Renderizado Virtual de Componentes

El punto de entrada para cualquier prueba con React Testing Library es la función render. Esta utilidad monta el componente en un DOM virtual y devuelve un objeto con múltiples métodos de consulta.

import { render, screen } from "@testing-library/react";
import App from "./App";

test("renderiza enlace de aprendizaje", () => {
    render(<App />);
    const enlace = screen.getByText(/learn react/i);
    expect(enlace).toBeInTheDocument();
});

La función render realiza varias operaciones críticas:

  • Crea un contenedor virtual en document.body
  • Monta el componente con sus proveedores de contexto
  • Ejecuta los efectos de useEffect
  • Devuelve utilidades para consultar el DOM resultante

El objeto screen actúa como punto central de acceso a todos los métodos de consulta, eliminando la necesidad de destructurar múltiples valores del resultado de render.

Métodos de Consulta por Atributos Semánticos

React Testing Library ofrece una jerarquía clara de métodos de consulta, priorizando aquellos que mejor representan cómo un usuario encontraría un elemento en la interfaz.

Consulta por Texto Visible

El método getByText localiza elementos basándose en su contenido de texto visible:

const titulo = screen.getByText("Bienvenido a mi aplicación");
expect(titulo).toBeVisible();

Este enfoque es particularmente efectivo para encabezados, párrafos y texto de botones.

Consulta por Rol Accesible

getByRole utiliza los roles ARIA implícitos y explícitos del HTML:

const boton = screen.getByRole("button", { name: /guardar/i });
userEvent.click(boton);

Los roles más comunes incluyen button, link, heading, textbox, y checkbox. Este método es el recomendado como primera opción por su alineación con estándares de accesibilidad.

Consulta por Etiqueta de Formulario

Para campos de formulario, getByLabelText encuentra inputs asociados a sus etiquetas:

const inputEmail = screen.getByLabelText("Correo electrónico");
await userEvent.type(inputEmail, "[email protected]");

Consulta por Placeholder

Los placeholders proporcionan otra vía de localización:

const busqueda = screen.getByPlaceholderText("Buscar productos...");

Consulta por Texto Alternativo

Para imágenes, getByAltText es el método apropiado:

const logo = screen.getByAltText("Logo de la empresa");
expect(logo).toHaveAttribute("src", "logo.png");

Variantes de Métodos de Consulta

Cada método de consulta tiene tres variantes principales:

  • getBy*: Lanza error si no encuentra el elemento o encuentra múltiples
  • queryBy*: Devuelve null si no encuentra el elemento, útil para aserciones negativas
  • findBy*: Versión asíncrona que espera a que aparezca el elemento
// Espera hasta 5 segundos por un mensaje de éxito
const mensaje = await screen.findByText(
    "Operación completada",
    {},
    { timeout: 5000 }
);

Patrones de Consulta Compuestos

En interfaces complejas, es común necesitar combinar criterios. React Testing Library permite encadenar consultas:

const botonGuardar = screen.getByRole("button", {
    name: /guardar cambios/i,
});
within(botonGuardar).getByText("Guardar");

El helper within restringe las consultas subsiguientes a un subárbol del DOM.

Simulación de Interacciones del Usuario

La biblioteca user-event representa la evolución de fireEvent, proporcionando una API más realista para simular interacciones.

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

test("cambia tema al hacer clic", async () => {
    render(<TemaBoton />);
    const boton = screen.getByRole("button", { name: /tema actual: claro/i });

    await userEvent.click(boton);

    expect(boton).toHaveTextContent(/oscuro/i);
});

user-event simula eventos del navegador en el orden correcto: mouseover, mousemove, mousedown, mouseup, click. Esto garantina que los efectos hover y focus se activen apropiadamente.

Interacciones con Formularios

La escritura en campos de texto requiere el helper type:

test("completa formulario de contacto", async () => {
    render(<FormularioContactoôr />);

    await userEvent.type(
        screen.getByLabelText("Nombre completo"),
        "Juan Pérez"
    );

    await userEvent.selectOptions(screen.getByLabelText("País"), "ES");

    await userEvent.click(screen.getByRole("button", { name: "Enviar" }));
});

Interacciones Avanzadas

user-event v14 introdujo métodos para interacciones más complejas:

// Copiar y pegar
await userEvent.copy(screen.getByText("Texto a copiar"));
await userEvent.paste(screen.getByLabelText("Destino"));

// Arrastrar y soltar
await userEvent.pointer({
    keys: "[MouseLeft]",
    target: elementoOrigen,
});
await userEvent.pointer({
    coords: { x: 100, y: 200 },
});

Pruebas de Componentes con Estado

Los componentes que mantienen estado interno requieren secuencias de interacciones para validar su comportamiento completo.

function Contador() {
    const [cuenta, setCuenta] = useState(0);

    return (
        <div>
            <p data-testid="valor">Cuenta: {cuenta}</p>
            <button onClick={() => setCuenta((c) => c + 1)}>Incrementar</button>
        </div>
    );
}

test("incrementa contador correctamente", async () => {
    render(<Contador />);

    const valor = screen.getByTestId("valor");
    const boton = screen.getByRole("button", { name: "Incrementar" });

    expect(valor).toHaveTextContent("Cuenta: 0");

    await userEvent.click(boton);
    expect(valor).toHaveTextContent("Cuenta: 1");

    await userEvent.dblClick(boton);
    expect(valor).toHaveTextContent("Cuenta: 3");
});

Manejo de Operaciones Asíncronas

Las pruebas que involucran datos asíncronos requieren el uso de findBy* o waitFor:

test("muestra datos cargados", async () => {
    render(<ListaUsuarios />);

    expect(screen.getByText("Cargando...")).toBeInTheDocument();

    const usuario = await screen.findByText("María González");
    expect(usuario).toBeVisible();

    expect(screen.queryByText("Cargando...")).not.toBeInTheDocument();
});

La función waitFor permite mayor control sobre las condiciones de espera:

await waitFor(
    () => {
        expect(screen.getByTestId("lista-completa")).toBeInTheDocument();
    },
    { timeout: 3000 }
);

Mocking de Módulos y API

En aplicaciones reales, los componentes consumen datos externos. Jest permite mockear estos módulos:

// __mocks__/api.js
export const obtenerUsuarios = jest.fn(() =>
    Promise.resolve([
        { id: 1, nombre: "Ana" },
        { id: 2, nombre: "Carlos" },
    ])
);

En las pruebas:

jest.mock("../api");

test("renderiza usuarios desde API", async () => {
    render(<ListaUsuarios />);

    expect(await screen.findByText("Ana")).toBeInTheDocument();
    expect(screen.getByText("Carlos")).toBeInTheDocument();
});

Mejores Prácticas Actualizadas 2025

Priorizar Consultas Accesibles

La jerarquía recomendada en 2025 es:

  1. getByRole
  2. getByLabelText
  3. getByText (para contenido visible)
  4. getByTestId (solo como último recurso)

Evitar Selectores de Implementación

Las pruebas no deben depender de clases CSS generadas, nombres de variables de estado, o estructura interna de componentes.

// ❌ Mal - depende de implementación
const { container } = render(<Componente />);
expect(container.querySelector(".css-abc123")).toBeTruthy();

// ✅ Bien - enfocado en comportamiento visible
expect(screen.getByRole("alert")).toHaveTextContent("Error de validación");

Limpieza Automática

React Testing Library incluye cleanup automático entre pruebas, pero en configuraciones personalizadas:

afterEach(() => {
    cleanup();
});

Pruebas de Accesibilidad Integradas

La combinación de Testing Library con jest-axe permite detectar problemas de accesibilidad:

import { axe, toHaveNoViolations } from "jest-axe";

expect.extend(toHaveNoViolations);

test("cumple estándares de accesibilidad", async () => {
    const { container } = render(<Formulario />);
    const results = await axe(container);
    expect(results).toHaveNoViolations();
});

Estructura de Archivos de Pruebas

La convención actual recomienda colocar archivos de pruebas junto a los componentes:

src/
  components/
    Button/
      Button.jsx
      Button.test.jsx
      Button.stories.jsx
    Input/
      Input.jsx
      Input.test.jsx

Esto facilita el mantenimiento y la navegación del código.

Testing de Hooks Personalizados

Los hooks personalizados se prueban con @testing-library/react-hooks:

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

test("useContador funciona correctamente", () => {
    const { result } = renderHook(() => useContador(5));

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

    act(() => {
        result.current.incrementar();
    });

    expect(result.current.cuenta).toBe(6);
});

Pruebas de Rendimiento y Memoria

Aunque no es el foco principal de Testing Library, se pueden integrar mediciones:

test("no crea memory leaks", () => {
    const { unmount } = render(<ComponenteConSuscripciones />);

    // Forzar recolección de basura
    global.gc();
    const antes = performance.memory.usedJSHeapSize;

    unmount();
    global.gc();

    const despues = performance.memory.usedJSHeapSize;
    expect(despues).toBeLessThan(antes);
});

Integración con CI/CD

En pipelines modernos, las pruebas de Testing Library se ejecutan en modo watchless:

# .github/workflows/test.yml
- name: Ejecutar pruebas
  run: npm test -- --coverage --watchAll=false

La cobertura de código se ha convertido en métrica estándar, con umbrales típicos del 80-90% para líneas y funciones.

Herramientas Complementarias

Testing Library para Vue y Svelte

El mismo paradigma se aplica a otros frameworks:

npm install --save-dev @testing-library/vue
npm install --save-dev @testing-library/svelte

Playwright para E2E

Testing Library se complementa perfectamente con pruebas end-to-end:

// e2e/login.spec.js
test("login exitoso", async ({ page }) => {
    await page.goto("/login");
    await page.getByLabelText("Usuario").fill("admin");
    await page.getByLabelText("Contraseña").fill("secret");
    await page.getByRole("button", { name: "Iniciar sesión" }).click();

    await expect(page.getByText("Bienvenido")).toBeVisible();
});

Debugging Efectivo

La función screen.debug() sigue siendo invaluable:

test("debug de estado complejo", () => {
    render(<Dashboard />);
    screen.debug();

    // Debug específico
    screen.debug(screen.getByTestId("widget-venta"));
});

En entornos CI, se pueden generar snapshots HTML para debugging posterior.

Patrones Avanzados

Testing de Lazy Loading

test("carga componente diferida", async () => {
    const LazyComponent = await import("./LazyComponent");
    render(<LazyComponent />);

    await waitFor(() =>
        expect(screen.getByText("Componente cargado")).toBeInTheDocument()
    );
});

Testing de Portales

Los portales requieren configuración especial del contenedor:

test("renderiza modal en portal", () => {
    const portalRoot = document.createElement("div");
    portalRoot.setAttribute("id", "modal-root");
    document.body.appendChild(portalRoot);

    render(<App />);

    expect(screen.getByText("Título del modal")).toBeInTheDocument();
});

Conclusiones

React Testing Library en 2025 representa la madurez de un ecosistema de pruebas que ha evolucionado desde enfoques centrados en implementación hacia un paradigma centrado en el usuario. Su filosofía de pruebas que simulan comportamiento real no solo mejora la calidad del código, sino que fomenta prácticas de desarrollo más accesibles y mantenibles.

La integración fluida con herramientas modernas, la extensibilidad mediante complementos, y el enfoque implacable en la experiencia del usuario final posicionan a Testing Library como la opción definitiva para pruebas de componentes React.

Al adoptar estas prácticas, los equipos no solo reducen bugs en producción, sino que crean una base sólida para la evolución continua de sus aplicaciones. Las pruebas dejan de ser una carga para convertirse en el fundamento de la confianza en el despliegue continuo.

La inversión en pruebas bien escritas con React Testing Library genera retornos compuestos: menos tiempo en debugging, mayor velocidad de desarrollo, y aplicaciones que realmente funcionan como los usuarios esperan.


```markdown
---
title: "Aprende a Testear Aplicaciones React con Testing Library 2025"
description: "Guía completa y actualizada para dominar React Testing Library en 2025, con ejemplos prácticos, mejores prácticas y enfoque en pruebas que simulan comportamiento real del usuario en aplicaciones modernas."
slug: "aprende-testear-aplicaciones-react-testing-library-2025"
weight: 0
type: "post"
header: true
draft: false
tags:
    - "pruebas unitarias react"
    - "testing library avanzado"
    - "desarrollo frontend moderno"
    - "calidad codigo javascript"
    - "react testing library"
    - "pruebas automatizadas"
    - "eventos usuario"
    - "dom virtual"
    - "jest"
date: "2025-10-27 14:30:00"
---
## Introducción a las Pruebas en React con Testing Library

En el ecosistema actual del desarrollo frontend, donde React sigue dominando como una de las bibliotecas más utilizadas para construir interfaces de usuario, la calidad del código se ha convertido en un factor determinante para el éxito de cualquier proyecto. Las pruebas automatizadas no solo garantizan que las funcionalidades existentes sigan operando correctamente tras modificaciones, sino que también proporcionan una red de seguridad que permite a los equipos evolucionar sus aplicaciones con confianza.

React Testing Library emerge como la herramienta preferida en 2025 para escribir pruebas que realmente reflejan cómo los usuarios interactúan con las aplicaciones. A diferencia de enfoques tradicionales que se centran en detalles de implementación interna, esta biblioteca promueve un paradigma donde **las pruebas simulan usuarios reales** interactuando con elementos del DOM renderizados en el navegador.

Este enfoque filosófico no es casual. Al priorizar la experiencia del usuario final sobre la estructura interna de los componentes, se logra una mayor resiliencia ante cambios de refactorización. Un componente puede ser completamente reescrito utilizando diferentes hooks o, pero mientras mantenga el mismo comportamiento visible en la interfaz, las pruebas continuarán pasando.

## Configuración Inicial del Entorno de Pruebas

La integración de React Testing Library en proyectos modernos se ha simplificado significativamente gracias a las herramientas de scaffolding actuales. Cuando se inicializa una aplicación React mediante Create React App o Vite, ambas incluyen por defecto Jest como motor de pruebas y React Testing Library como conjunto de utilidades para interactuar con el DOM.

Para proyectos que requieren configuración manual, la instalación se realiza mediante npm o yarn:

```bash
npm install --save-dev @testing-library/react @testing-library/jest-dom jest

Es importante destacar que React Testing Library no es un framework de pruebas completo, sino una biblioteca de utilidades. Jest proporciona el runner de pruebas, las aserciones y el entorno de ejecución, mientras que Testing Library ofrece los métodos para consultar y manipular el DOM virtual.

La configuración mínima requiere la importación de los matchers personalizados de jest-dom, que extienden las capacidades de aserción:

// src/setupTests.js
import "@testing-library/jest-dom";

Este archivo se ejecuta automáticamente antes de cada suite de pruebas cuando se utiliza Create React App.

Renderizado Virtual de Componentes

El punto de entrada para cualquier prueba con React Testing Library es la función render. Esta utilidad monta el componente en un DOM virtual y devuelve un objeto con múltiples métodos de consulta.

import { render, screen } from "@testing-library/react";
import App from "./App";

test("renderiza enlace de aprendizaje", () => {
    render(<App />);
    const enlace = screen.getByText(/learn react/i);
    expect(enlace).toBeInTheDocument();
});

La función render realiza varias operaciones críticas:

  • Crea un contenedor virtual en document.body
  • Monta el componente con sus proveedores de contexto
  • Ejecuta los efectos de useEffect
  • Devuelve utilidades para consultar el DOM resultante

El objeto screen actúa como punto central de acceso a todos los métodos de consulta, eliminando la necesidad de destructurar múltiples valores del resultado de render.

Métodos de Consulta por Atributos Semánticos

React Testing Library ofrece una jerarquía clara de métodos de consulta, priorizando aquellos que mejor representan cómo un usuario encontraría un elemento en la interfaz.

Consulta por Texto Visible

El método getByText localiza elementos basándose en su contenido de texto visible:

const titulo = screen.getByText("Bienvenido a mi aplicación");
expect(titulo).toBeVisible();

Este enfoque es particularmente efectivo para encabezados, párrafos y texto de botones.

Consulta por Rol Accesible

getByRole utiliza los roles ARIA implícitos y explícitos del HTML:

const boton = screen.getByRole("button", { name: /guardar/i });
userEvent.click(boton);

Los roles más comunes incluyen button, link, heading, textbox, y checkbox. Este método es el recomendado como primera opción por su alineación con estándares de accesibilidad.

Consulta por Etiqueta de Formulario

Para campos de formulario, getByLabelText encuentra inputs asociados a sus etiquetas:

const inputEmail = screen.getByLabelText("Correo electrónico");
await userEvent.type(inputEmail, "[email protected]");

Consulta por Placeholder

Los placeholders proporcionan otra vía de localización:

const busqueda = screen.getByPlaceholderText("Buscar productos...");

Consulta por Texto Alternativo

Para imágenes, getByAltText es el método apropiado:

const logo = screen.getByAltText("Logo de la empresa");
expect(logo).toHaveAttribute("src", "logo.png");

Variantes de Métodos de Consulta

Cada método de consulta tiene tres variantes principales:

  • getBy*: Lanza error si no encuentra el elemento o encuentra múltiples
  • queryBy*: Devuelve null si no encuentra el elemento, útil para aserciones negativas
  • findBy*: Versión asíncrona que espera a que aparezca el elemento
// Espera hasta 5 segundos por un mensaje de éxito
const mensaje = await screen.findByText(
    "Operación completada",
    {},
    { timeout: 5000 }
);

Patrones de Consulta Compuestos

En interfaces complejas, es común necesitar combinar criterios. React Testing Library permite encadenar consultas:

const botonGuardar = screen.getByRole("button", {
    name: /guardar cambios/i,
});
within(botonGuardar).getByText("Guardar");

El helper within restringe las consultas subsiguientes a un subárbol del DOM.

Simulación de Interacciones del Usuario

La biblioteca user-event representa la evolución de fireEvent, proporcionando una API más realista para simular interacciones.

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

test("cambia tema al hacer clic", async () => {
    render(<TemaBoton />);
    const boton = screen.getByRole("button", { name: /tema actual: claro/i });

    await userEvent.click(boton);

    expect(boton).toHaveTextContent(/oscuro/i);
});

user-event simula eventos del navegador en el orden correcto: mouseover, mousemove, mousedown, mouseup, click. Esto garantina que los efectos hover y focus se activen apropiadamente.

Interacciones con Formularios

La escritura en campos de texto requiere el helper type:

test("completa formulario de contacto", async () => {
    render(<FormularioContacto />);

    await userEvent.type(
        screen.getByLabelText("Nombre completo"),
        "Juan Pérez"
    );

    await userEvent.selectOptions(screen.getByLabelText("País"), "ES");

    await userEvent.click(screen.getByRole("button", { name: "Enviar" }));
});

Interacciones Avanzadas

user-event v14 introdujo métodos para interacciones más complejas:

// Copiar y pegar
await userEvent.copy(screen.getByText("Texto a copiar"));
await userEvent.paste(screen.getByLabelText("Destino"));

// Arrastrar y soltar
await userEvent.pointer({
    keys: "[MouseLeft]",
    target: elementoOrigen,
});
await userEvent.pointer({
    coords: { x: 100, y: 200 },
});

Pruebas de Componentes con Estado

Los componentes que mantienen estado interno requieren secuencias de interacciones para validar su comportamiento completo.

function Contador() {
    const [cuenta, setCuenta] = useState(0);

    return (
        <div>
            <p data-testid="valor">Cuenta: {cuenta}</p>
            <button onClick={() => setCuenta((c) => c + 1)}>Incrementar</button>
        </div>
    );
}

test("incrementa contador correctamente", async () => {
    render(<Contador />);

    const valor = screen.getByTestId("valor");
    const boton = screen.getByRole("button", { name: "Incrementar" });

    expect(valor).toHaveTextContent("Cuenta: 0");

    await userEvent.click(boton);
    expect(valor).toHaveTextContent("Cuenta: 1");

    await userEvent.dblClick(boton);
    expect(valor).toHaveTextContent("Cuenta: 3");
});

Manejo de Operaciones Asíncronas

Las pruebas que involucran datos asíncronos requieren el uso de findBy* o waitFor:

test("muestra datos cargados", async () => {
    render(<ListaUsuarios />);

    expect(screen.getByText("Cargando...")).toBeInTheDocument();

    const usuario = await screen.findByText("María González");
    expect(usuario).toBeVisible();

    expect(screen.queryByText("Cargando...")).not.toBeInTheDocument();
});

La función waitFor permite mayor control sobre las condiciones de espera:

await waitFor(
    () => {
        expect(screen.getByTestId("lista-completa")).toBeInTheDocument();
    },
    { timeout: 3000 }
);

Mocking de Módulos y API

En aplicaciones reales, los componentes consumen datos externos. Jest permite mockear estos módulos:

// __mocks__/api.js
export const obtenerUsuarios = jest.fn(() =>
    Promise.resolve([
        { id: 1, nombre: "Ana" },
        { id: 2, nombre: "Carlos" },
    ])
);

En las pruebas:

jest.mock("../api");

test("renderiza usuarios desde API", async () => {
    render(<ListaUsuarios />);

    expect(await screen.findByText("Ana")).toBeInTheDocument();
    expect(screen.getByText("Carlos")).toBeInTheDocument();
});

Mejores Prácticas Actualizadas 2025

Priorizar Consultas Accesibles

La jerarquía recomendada en 2025 es:

  1. getByRole
  2. getByLabelText
  3. getByText (para contenido visible)
  4. getByTestId (solo como último recurso)

Evitar Selectores de Implementación

Las pruebas no deben depender de clases CSS generadas, nombres de variables de estado, o estructura interna de componentes.

// ❌ Mal - depende de implementación
const { container } = render(<Componente />);
expect(container.querySelector(".css-abc123")).toBeTruthy();

// ✅ Bien - enfocado en comportamiento visible
expect(screen.getByRole("alert")).toHaveTextContent("Error de validación");

Limpieza Automática

React Testing Library incluye cleanup automático entre pruebas, pero en configuraciones personalizadas:

afterEach(() => {
    cleanup();
});

Pruebas de Accesibilidad Integradas

La combinación de Testing Library con jest-axe permite detectar problemas de accesibilidad:

import { axe, toHaveNoViolations } from "jest-axe";

expect.extend(toHaveNoViolations);

test("cumple estándares de accesibilidad", async () => {
    const { container } = render(<Formulario />);
    const results = await axe(container);
    expect(results).toHaveNoViolations();
});

Estructura de Archivos de Pruebas

La convención actual recomienda colocar archivos de pruebas junto a los componentes:

src/
  components/
    Button/
      Button.jsx
      Button.test.jsx
      Button.stories.jsx
    Input/
      Input.jsx
      Input.test.jsx

Esto facilita el mantenimiento y la navegación del código.

Testing de Hooks Personalizados

Los hooks personalizados se prueban con @testing-library/react-hooks:

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

test("useContador funciona correctamente", () => {
    const { result } = renderHook(() => useContador(5));

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

    act(() => {
        result.current.incrementar();
    });

    expect(result.current.cuenta).toBe(6);
});

Pruebas de Rendimiento y Memoria

Aunque no es el foco principal de Testing Library, se pueden integrar mediciones:

test("no crea memory leaks", () => {
    const { unmount } = render(<ComponenteConSuscripciones />);

    // Forzar recolección de basura
    global.gc();
    const antes = performance.memory.usedJSHeapSize;

    unmount();
    global.gc();

    const despues = performance.memory.usedJSHeapSize;
    expect(despues).toBeLessThan(antes);
});

Integración con CI/CD

En pipelines modernos, las pruebas de Testing Library se ejecutan en modo watchless:

# .github/workflows/test.yml
- name: Ejecutar pruebas
  run: npm test -- --coverage --watchAll=false

La cobertura de código se ha convertido en métrica estándar, con umbrales típicos del 80-90% para líneas y funciones.

Herramientas Complementarias

Testing Library para Vue y Svelte

El mismo paradigma se aplica a otros frameworks:

npm install --save-dev @testing-library/vue
npm install --save-dev @testing-library/svelte

Playwright para E2E

Testing Library se complementa perfectamente con pruebas end-to-end:

// e2e/login.spec.js
test("login exitoso", async ({ page }) => {
    await page.goto("/login");
    await page.getByLabelText("Usuario").fill("admin");
    await page.getByLabelText("Contraseña").fill("secret");
    await page.getByRole("button", { name: "Iniciar sesión" }).click();

    await expect(page.getByText("Bienvenido")).toBeVisible();
});

Debugging Efectivo

La función screen.debug() sigue siendo invaluable:

test("debug de estado complejo", () => {
    render(<Dashboard />);
    screen.debug();

    // Debug específico
    screen.debug(screen.getByTestId("widget-venta"));
});

En entornos CI, se pueden generar snapshots HTML para debugging posterior.

Patrones Avanzados

Testing de Lazy Loading

test("carga componente diferida", async () => {
    const LazyComponent = await import("./LazyComponent");
    render(<LazyComponent />);

    await waitFor(() =>
        expect(screen.getByText("Componente cargado")).toBeInTheDocument()
    );
});

Testing de Portales

Los portales requieren configuración especial del contenedor:

test("renderiza modal en portal", () => {
    const portalRoot = document.createElement("div");
    portalRoot.setAttribute("id", "modal-root");
    document.body.appendChild(portalRoot);

    render(<App />);

    expect(screen.getByText("Título del modal")).toBeInTheDocument();
});

Conclusiones

React Testing Library en 2025 representa la madurez de un ecosistema de pruebas que ha evolucionado desde enfoques centrados en implementación hacia un paradigma centrado en el usuario. Su filosofía de pruebas que simulan comportamiento real no solo mejora la calidad del código, sino que fomenta prácticas de desarrollo más accesibles y mantenibles.

La integración fluida con herramientas modernas, la extensibilidad mediante complementos, y el enfoque implacable en la experiencia del usuario final posicionan a Testing Library como la opción definitiva para pruebas de componentes React.

Al adoptar estas prácticas, los equipos no solo reducen bugs en producción, sino que crean una base sólida para la evolución continua de sus aplicaciones. Las pruebas dejan de ser una carga para convertirse en el fundamento de la confianza en el despliegue continuo.

La inversión en pruebas bien escritas con React Testing Library genera retornos compuestos: menos tiempo en debugging, mayor velocidad de desarrollo, y aplicaciones que realmente funcionan como los usuarios esperan.