TechLead

Probando Aplicaciones React

Pruebas unitarias, pruebas de componentes y mocking con Jest y React Testing Library

¿Por qué Probar Apps React?

Las pruebas aseguran que tus componentes funcionen correctamente, previenen regresiones y te dan confianza para refactorizar. El ecosistema React tiene excelentes herramientas: Jest como ejecutor de pruebas y React Testing Library (RTL) para pruebas de componentes.

🧪 Filosofía de Pruebas

"Cuanto más se parezcan tus pruebas a la forma en que se usa tu software, más confianza te pueden dar."

— Kent C. Dodds, creador de React Testing Library

Tu Primera Prueba de Componente

// Greeting.jsx
export function Greeting({ name }) {
  return <h1>Hola, {name}!</h1>;
}

// Greeting.test.jsx
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import { Greeting } from './Greeting';

test('renderiza saludo con nombre', () => {
  render(<Greeting name="Alice" />);
  const heading = screen.getByRole('heading');
  expect(heading).toHaveTextContent('Hola, Alice!');
});

Probando Interacciones del Usuario

import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Counter } from './Counter';

test('incrementa y decrementa contador', async () => {
  const user = userEvent.setup();
  render(<Counter />);

  expect(screen.getByText('Conteo: 0')).toBeInTheDocument();

  await user.click(screen.getByText('Incrementar'));
  await user.click(screen.getByText('Incrementar'));
  expect(screen.getByText('Conteo: 2')).toBeInTheDocument();

  await user.click(screen.getByText('Decrementar'));
  expect(screen.getByText('Conteo: 1')).toBeInTheDocument();
});

Mockeando Llamadas API

import { render, screen, waitFor } from '@testing-library/react';
import { UserList } from './UserList';

beforeEach(() => {
  global.fetch = jest.fn();
});

test('renderiza usuarios después de cargar', async () => {
  fetch.mockResolvedValueOnce({
    json: () => Promise.resolve([
      { id: 1, name: 'Alice' },
      { id: 2, name: 'Bob' },
    ]),
  });

  render(<UserList />);
  expect(screen.getByText('Cargando usuarios...')).toBeInTheDocument();

  await waitFor(() => {
    expect(screen.getByText('Alice')).toBeInTheDocument();
    expect(screen.getByText('Bob')).toBeInTheDocument();
  });
});

test('muestra error en fallo de fetch', async () => {
  fetch.mockRejectedValueOnce(new Error('Error de red'));
  render(<UserList />);

  await waitFor(() => {
    expect(screen.getByRole('alert')).toHaveTextContent('Error: Error de red');
  });
});

Probando Custom Hooks

import { renderHook, act } from '@testing-library/react';
import { useCounter } from './useCounter';

test('inicializa con valor por defecto', () => {
  const { result } = renderHook(() => useCounter());
  expect(result.current.count).toBe(0);
});

test('incrementa y decrementa', () => {
  const { result } = renderHook(() => useCounter(5));

  act(() => result.current.increment());
  expect(result.current.count).toBe(6);

  act(() => result.current.reset());
  expect(result.current.count).toBe(5);
});

✅ Mejores Practicas de Pruebas

  • • Consulta elementos por rol y etiqueta primero
  • • Prueba comportamiento, no implementación
  • • Usa userEvent sobre fireEvent
  • • Usa waitFor o findBy para operaciones asíncronas
  • • Mantén las pruebas enfocadas — un comportamiento por prueba
  • • Mockea en el límite (llamadas API, no funciones internas)