Introducción
El código React sin pruebas es un pasivo. Refactorizas un componente y algo se rompe en tres pantallas de distancia. Jest es el framework de pruebas que viene incluido en Create React App y se integra estrechamente con React Testing Library. Se encarga del ejecutor de pruebas, las aserciones, el mocking y los informes de cobertura en un único paquete.
¿Qué es Jest?
Jest es un framework de pruebas open-source desarrollado por Facebook para aplicaciones JavaScript. Requiere casi ninguna configuración para empezar, lo cual es poco común entre las herramientas de prueba.
Características Principales de Jest
- Cero Configuración: Jest requiere una configuración mínima, lo que te permite empezar a probar rápidamente.
- Snapshot Testing: Captura la salida renderizada de los componentes React para detectar cambios en la UI.
- Pruebas Aisladas: Cada prueba se ejecuta en su propio sandbox para evitar interferencias entre pruebas.
- Mocking Potente: Haz mock de funciones, módulos y timers para controlar el comportamiento del código durante las pruebas.
- Cobertura de Código: Genera informes detallados para entender qué parte de tu código está siendo probada.
¿Por Qué Usar Jest para Probar Aplicaciones React?
Jest se combina naturalmente con React Testing Library, que te permite consultar e interactuar con tus componentes de la forma en que lo haría un usuario, en lugar de depender de detalles de implementación. Las pruebas se ejecutan en paralelo, por lo que incluso las suites grandes terminan rápidamente. La API de mocking es suficientemente completa para aislar cualquier dependencia.
Configurando Jest en una Aplicación React
Requisitos Previos
Node.js y npm instalados, con familiaridad básica con React y JavaScript.
Creando una Nueva App React
Crea una nueva aplicación React usando Create React App:
npx create-react-app my-app
cd my-app
Create React App viene con Jest preconfigurado, por lo que puedes empezar a escribir pruebas de inmediato.
Aplicaciones React Existentes
Si tienes un proyecto React existente sin Jest, instálalo:
npm install --save-dev jest babel-jest @babel/preset-env @babel/preset-react react-test-renderer
Luego añade lo siguiente a tu package.json:
"scripts": {
"test": "jest"
},
"jest": {
"setupFilesAfterEnv": ["<rootDir>/src/setupTests.js"]
}
Escribiendo Tu Primera Prueba
Creando un Componente React
Crea un nuevo archivo src/components/Greeting.js:
import React from 'react';
function Greeting({ name }) {
return <h1>Hello, {name}!</h1>;
}
export default Greeting;
Escribiendo una Prueba para el Componente
Crea un archivo de prueba src/components/Greeting.test.js:
import React from 'react';
import { render, screen } from '@testing-library/react';
import Greeting from './Greeting';
test('renders greeting message', () => {
render(<Greeting name="World" />);
const greetingElement = screen.getByText(/Hello, World!/i);
expect(greetingElement).toBeInTheDocument();
});
Ejecutando la Prueba
npm test
Jest encuentra los archivos de prueba automáticamente y muestra los resultados en el terminal.
Entendiendo la Sintaxis de las Pruebas
La prueba anterior sigue un patrón simple. Importa el componente, renderízalo en un DOM virtual mediante render(), consulta un elemento con screen.getByText() y verifica su existencia con expect().toBeInTheDocument(). La función test() envuelve cada caso con una cadena descriptiva y un callback.
Probando Interacciones del Usuario
Ejemplo: Evento de Clic en un Botón
Supongamos que tenemos un botón que incrementa un contador:
// Counter.js
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
export default Counter;
Escribiendo Pruebas para la Interacción del Usuario
// Counter.test.js
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import Counter from './Counter';
test('increments count on button click', () => {
render(<Counter />);
const buttonElement = screen.getByText(/Click me/i);
fireEvent.click(buttonElement);
expect(screen.getByText(/You clicked 1 times/i)).toBeInTheDocument();
});
fireEvent.click() simula un clic real y luego verificamos que el contador mostrado se actualizó. La prueba fallaría si el handler del onClick estuviera roto o si la actualización de estado no disparara un re-renderizado.
Snapshot Testing
El snapshot testing registra la salida renderizada de un componente y falla si cambia inesperadamente. Es útil para detectar regresiones visuales no intencionadas.
Creando una Prueba de Snapshot
// Greeting.test.js
import React from 'react';
import renderer from 'react-test-renderer';
import Greeting from './Greeting';
test('Greeting component matches snapshot', () => {
const tree = renderer.create(<Greeting name="World" />).toJSON();
expect(tree).toMatchSnapshot();
});
La primera ejecución crea un archivo de snapshot. Cada ejecución posterior lo compara con él. Cuando cambies intencionalmente la salida, actualiza el snapshot con jest --updateSnapshot.
Mocking de Funciones y Módulos
Mockear un Módulo
Supongamos que tu componente usa un módulo de API:
// api.js
export const fetchData = () => {
// fetch data from an API
};
Haz el mock en tus pruebas para que las llamadas de red nunca se ejecuten realmente:
jest.mock('./api', () => ({
fetchData: jest.fn(() => Promise.resolve({ data: 'mock data' })),
}));
Usando el Mock en una Prueba
// DataComponent.test.js
import React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import DataComponent from './DataComponent';
import { fetchData } from './api';
test('displays fetched data', async () => {
fetchData.mockResolvedValueOnce({ data: 'mock data' });
render(<DataComponent />);
await waitFor(() => expect(screen.getByText(/mock data/i)).toBeInTheDocument());
});
Consejos para Pruebas Efectivas
- Prueba tu propio código, no las bibliotecas de terceros.
- Escribe pruebas deterministas. Cualquier prueba que produzca resultados diferentes en distintas ejecuciones acabará socavando la confianza en la suite.
- Prueba la salida que ve el usuario, no los detalles de implementación internos. Si refactorizas los internos sin cambiar el comportamiento, ninguna prueba debería romperse.
- Usa nombres descriptivos para las pruebas. "increments count on button click" es más útil que "test 1".
Informes de Cobertura de Código
Jest genera informes de cobertura que muestran qué líneas, ramas y funciones ejercitan tus pruebas.
Habilitando la Cobertura de Código
npm test -- --coverage
Revisa la salida para encontrar rutas no probadas, especialmente ramas de error que solo se activan bajo condiciones específicas.
Integración con Integración Continua (CI)
Ejecuta Jest en tu pipeline de CI para que cada pull request incluya una puerta de pruebas.
Ejemplo con GitHub Actions
Crea un archivo .github/workflows/main.yml:
name: Node.js CI
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14.x, 16.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js $
uses: actions/setup-node@v2
with:
node-version: $
- run: npm install
- run: npm test -- --coverage
- uses: actions/upload-artifact@v2
with:
name: coverage-report
path: coverage/
Conclusión
Jest cubre todo lo que necesita un flujo de trabajo de pruebas React: un ejecutor de pruebas paralelo y rápido, una biblioteca de aserciones, mocking, comparación de snapshots e informes de cobertura. Empieza con las pruebas de interacción — capturan más regresiones por línea de código de prueba escrita — y luego añade snapshots para los componentes de UI estables. Una vez que las pruebas se ejecuten automáticamente en CI, obtendrás el loop de feedback permanente que hace que la refactorización sea segura.