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
userEventsobrefireEvent - • Usa
waitForofindBypara operaciones asíncronas - • Mantén las pruebas enfocadas — un comportamiento por prueba
- • Mockea en el límite (llamadas API, no funciones internas)