TL;DR
- Jest es un framework de testing sin configuración que incluye assertions, mocking y cobertura
- Matchers como
toBe,toEqual,toContainhacen assertions legibles- Mockea funciones con
jest.fn(), módulos conjest.mock(), timers conjest.useFakeTimers()- Testing async: usa
async/await,resolves/rejects, o callbackdone- Snapshot testing captura UI — útil para componentes React
Ideal para: Desarrolladores JavaScript/TypeScript, proyectos React/Vue/Node.js, equipos queriendo todo-en-uno Omite si: Necesitas testing basado en navegador (usa Playwright/Cypress en su lugar)
Jest es el framework de testing JavaScript más utilizado, consistentemente clasificado en el primer lugar en la encuesta State of JS 2024 en todas las categorías de herramientas de testing. Con más de 44.000 estrellas en GitHub y decenas de millones de descargas semanales en npm, Jest incluye todo lo que un proyecto JavaScript necesita para testing unitario e integración en una sola dependencia: un test runner, una librería de assertions basada en expect(), un sistema de mocking que cubre funciones, módulos y timers, cobertura de código incorporada vía instrumentación V8, y snapshot testing para capturar output renderizado. Creado por Facebook y ahora mantenido por la comunidad open-source bajo la OpenJS Foundation, Jest funciona con React, Vue, Angular, Node.js y cualquier codebase JavaScript o TypeScript. La configuración cero significa que la mayoría de los proyectos pueden ejecutar su primera suite de tests en minutos tras la instalación. La documentación oficial en jestjs.io/docs/getting-started cubre cada API en detalle. Este tutorial enseña Jest desde los primeros principios — matchers, patrones de testing async, estrategias de mocking, flujos de snapshot y configuración de cobertura — con los ejemplos prácticos y mejores prácticas que hacen las suites de tests mantenibles a escala.
“Los unit tests solo tienen valor si los developers confían en ellos y los ejecutan de verdad. El watch mode de Jest y el feedback casi instantáneo resuelven ese problema de confianza — cuando los tests corren en menos de un segundo, los developers los ejecutan antes de cada commit en lugar de tratar el CI como la primera señal de un build roto.” — Yuri Kan, Senior QA Lead
¿Qué es Jest?
Jest es un framework de testing JavaScript creado por Facebook. Con 44K+ stars en GitHub y decenas de millones de weekly downloads, ejecuta tus tests, provee assertions, mockea dependencias y genera reportes de cobertura — todo en un paquete. Según State of JS 2024, el 71% de los developers JavaScript usa Jest, lo que lo convierte en el framework de testing más popular. La API completa está documentada en jestjs.io/docs/getting-started.
Qué incluye Jest:
- Test runner — encuentra y ejecuta archivos de test
- Librería de assertions —
expect()con matchers incluidos - Mocking — mock de funciones, módulos, timers
- Cobertura — reportes de cobertura de código incluidos
- Snapshot testing — captura y compara output
- Watch mode — re-ejecuta tests al cambiar archivos
Instalación y Setup
Proyecto Nuevo
# Inicializar proyecto
npm init -y
# Instalar Jest
npm install --save-dev jest
# Agregar script de test a package.json
npm pkg set scripts.test="jest"
Proyecto TypeScript
npm install --save-dev jest typescript ts-jest @types/jest
# Inicializar config de ts-jest
npx ts-jest config:init
Create React App
Jest ya está incluido. Solo ejecuta:
npm test
Estructura del Proyecto
my-project/
├── src/
│ ├── calculator.js
│ └── utils/
│ └── formatters.js
├── __tests__/
│ ├── calculator.test.js
│ └── utils/
│ └── formatters.test.js
├── jest.config.js
└── package.json
Jest encuentra tests en:
- Archivos terminando en
.test.jso.spec.js - Archivos en carpetas
__tests__
Escribiendo Tu Primer Test
// src/calculator.js
function add(a, b) {
return a + b;
}
function divide(a, b) {
if (b === 0) {
throw new Error('Cannot divide by zero');
}
return a / b;
}
module.exports = { add, divide };
// __tests__/calculator.test.js
const { add, divide } = require('../src/calculator');
describe('Calculator', () => {
describe('add', () => {
test('suma dos números positivos', () => {
expect(add(2, 3)).toBe(5);
});
test('suma números negativos', () => {
expect(add(-1, -1)).toBe(-2);
});
test('suma con cero', () => {
expect(add(5, 0)).toBe(5);
});
});
describe('divide', () => {
test('divide dos números', () => {
expect(divide(10, 2)).toBe(5);
});
test('lanza error al dividir por cero', () => {
expect(() => divide(10, 0)).toThrow('Cannot divide by zero');
});
});
});
Ejecutando Tests
# Ejecutar todos los tests
npm test
# Ejecutar archivo específico
npm test -- calculator.test.js
# Ejecutar tests que coincidan con patrón
npm test -- --testNamePattern="suma"
# Watch mode
npm test -- --watch
# Con cobertura
npm test -- --coverage
Matchers
Los matchers son métodos para verificar valores. Jest tiene 50+ matchers incluidos. La lista completa está en la referencia de expect de Jest.
Matchers Comunes
// Igualdad
expect(2 + 2).toBe(4); // Igualdad estricta (===)
expect({ a: 1 }).toEqual({ a: 1 }); // Igualdad profunda
// Truthiness
expect(true).toBeTruthy();
expect(false).toBeFalsy();
expect(null).toBeNull();
expect(undefined).toBeUndefined();
expect('value').toBeDefined();
// Números
expect(10).toBeGreaterThan(5);
expect(10).toBeGreaterThanOrEqual(10);
expect(5).toBeLessThan(10);
expect(0.1 + 0.2).toBeCloseTo(0.3); // Punto flotante
// Strings
expect('Hello World').toMatch(/World/);
expect('Hello World').toContain('World');
// Arrays
expect([1, 2, 3]).toContain(2);
expect([1, 2, 3]).toHaveLength(3);
expect(['a', 'b']).toEqual(expect.arrayContaining(['a']));
// Objetos
expect({ a: 1, b: 2 }).toHaveProperty('a');
expect({ a: 1, b: 2 }).toHaveProperty('a', 1);
expect({ a: 1 }).toMatchObject({ a: 1 });
// Excepciones
expect(() => { throw new Error('fail'); }).toThrow();
expect(() => { throw new Error('fail'); }).toThrow('fail');
expect(() => { throw new Error('fail'); }).toThrow(Error);
Negando Matchers
Agrega .not antes de cualquier matcher:
expect(5).not.toBe(3);
expect([1, 2]).not.toContain(3);
expect({ a: 1 }).not.toHaveProperty('b');
Testing de Código Async
Async/Await
// src/api.js
async function fetchUser(id) {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
throw new Error('User not found');
}
return response.json();
}
module.exports = { fetchUser };
// __tests__/api.test.js
test('obtiene usuario exitosamente', async () => {
const user = await fetchUser(1);
expect(user.name).toBe('John');
});
test('lanza error para usuario inválido', async () => {
await expect(fetchUser(999)).rejects.toThrow('User not found');
});
Promesas con resolves/rejects
test('resuelve a datos de usuario', () => {
return expect(fetchUser(1)).resolves.toMatchObject({ name: 'John' });
});
test('rechaza para usuario faltante', () => {
return expect(fetchUser(999)).rejects.toThrow();
});
Estilo Callback (done)
function fetchDataWithCallback(callback) {
setTimeout(() => {
callback({ data: 'result' });
}, 100);
}
test('llama callback con datos', (done) => {
function callback(result) {
expect(result.data).toBe('result');
done(); // Test espera hasta que se llame done()
}
fetchDataWithCallback(callback);
});
Mocking
Mocking reemplaza implementaciones reales con sustitutos controlados. La guía de mock functions de Jest cubre todos los patrones de mocking en profundidad.
Mock Functions (jest.fn)
test('mock function trackea llamadas', () => {
const mockCallback = jest.fn(x => x + 1);
[1, 2, 3].forEach(mockCallback);
// Verificar cantidad de llamadas
expect(mockCallback).toHaveBeenCalledTimes(3);
// Verificar llamadas específicas
expect(mockCallback).toHaveBeenCalledWith(1);
expect(mockCallback).toHaveBeenLastCalledWith(3);
// Verificar valores de retorno
expect(mockCallback.mock.results[0].value).toBe(2);
});
Mock Valores de Retorno
const mock = jest.fn();
// Retornar diferentes valores en llamadas sucesivas
mock
.mockReturnValueOnce(10)
.mockReturnValueOnce(20)
.mockReturnValue(30);
console.log(mock()); // 10
console.log(mock()); // 20
console.log(mock()); // 30
console.log(mock()); // 30
// Mock promesas resolved/rejected
const asyncMock = jest.fn()
.mockResolvedValueOnce({ success: true })
.mockRejectedValueOnce(new Error('Failed'));
Mocking Módulos
// Mock módulo completo
jest.mock('./api');
const { fetchUser } = require('./api');
// Configurar implementación mock
fetchUser.mockResolvedValue({ id: 1, name: 'Mock User' });
test('usa API mockeado', async () => {
const user = await fetchUser(1);
expect(user.name).toBe('Mock User');
});
Mock con Factory
jest.mock('./database', () => ({
connect: jest.fn().mockResolvedValue(true),
query: jest.fn().mockResolvedValue([{ id: 1 }]),
close: jest.fn()
}));
Espiando Métodos
const video = {
play() {
return true;
}
};
test('espía método', () => {
const spy = jest.spyOn(video, 'play');
video.play();
expect(spy).toHaveBeenCalled();
expect(spy).toHaveReturnedWith(true);
spy.mockRestore(); // Restaurar implementación original
});
Mocking Timers
jest.useFakeTimers();
function delayedGreeting(callback) {
setTimeout(() => callback('Hello'), 1000);
}
test('llama callback después de delay', () => {
const callback = jest.fn();
delayedGreeting(callback);
expect(callback).not.toHaveBeenCalled();
jest.advanceTimersByTime(1000); // Avanzar tiempo
expect(callback).toHaveBeenCalledWith('Hello');
});
// O ejecutar todos los timers
test('con runAllTimers', () => {
const callback = jest.fn();
delayedGreeting(callback);
jest.runAllTimers();
expect(callback).toHaveBeenCalled();
});
Setup y Teardown
describe('Database tests', () => {
let db;
// Ejecuta una vez antes de todos los tests en este describe
beforeAll(async () => {
db = await connectToDatabase();
});
// Ejecuta una vez después de todos los tests
afterAll(async () => {
await db.close();
});
// Ejecuta antes de cada test
beforeEach(async () => {
await db.clear();
});
// Ejecuta después de cada test
afterEach(() => {
jest.clearAllMocks();
});
test('inserta registro', async () => {
await db.insert({ name: 'Test' });
const records = await db.findAll();
expect(records).toHaveLength(1);
});
});
Snapshot Testing
Los snapshots capturan output y detectan cambios no intencionales.
// src/formatUser.js
function formatUser(user) {
return {
displayName: `${user.firstName} ${user.lastName}`,
email: user.email.toLowerCase(),
initials: `${user.firstName[0]}${user.lastName[0]}`
};
}
module.exports = { formatUser };
// __tests__/formatUser.test.js
const { formatUser } = require('../src/formatUser');
test('formatea usuario correctamente', () => {
const user = {
firstName: 'John',
lastName: 'Doe',
email: 'John.Doe@Example.com'
};
expect(formatUser(user)).toMatchSnapshot();
});
Primera ejecución crea __snapshots__/formatUser.test.js.snap:
exports[`formatea usuario correctamente 1`] = `
{
"displayName": "John Doe",
"email": "john.doe@example.com",
"initials": "JD"
}
`;
Si el output cambia, el test falla. Actualiza snapshots:
npm test -- --updateSnapshot
# o
npm test -- -u
Cobertura de Código
# Generar reporte de cobertura
npm test -- --coverage
# Con umbrales específicos
npm test -- --coverage --coverageThreshold='{"global":{"branches":80,"functions":80}}'
Configuración de Cobertura
// jest.config.js
module.exports = {
collectCoverageFrom: [
'src/**/*.{js,jsx,ts,tsx}',
'!src/**/*.d.ts',
'!src/index.js'
],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80
}
},
coverageReporters: ['text', 'lcov', 'html']
};
Mejores Prácticas
1. Un Assertion Por Test (Usualmente)
// Malo: múltiples assertions no relacionados
test('validación de usuario', () => {
expect(isValidEmail('test@example.com')).toBe(true);
expect(isValidEmail('invalid')).toBe(false);
expect(isValidName('John')).toBe(true);
});
// Bueno: tests separados
test('acepta email válido', () => {
expect(isValidEmail('test@example.com')).toBe(true);
});
test('rechaza email inválido', () => {
expect(isValidEmail('invalid')).toBe(false);
});
2. Nombres de Tests Descriptivos
// Malo
test('test1', () => { ... });
// Bueno
test('retorna null cuando user ID no se encuentra', () => { ... });
3. Patrón Arrange-Act-Assert
test('calcula total con descuento', () => {
// Arrange
const cart = { items: [{ price: 100 }, { price: 50 }] };
const discount = 0.1;
// Act
const total = calculateTotal(cart, discount);
// Assert
expect(total).toBe(135);
});
4. Evita Testear Detalles de Implementación
// Malo: testea estado interno
test('establece flag interno', () => {
const counter = new Counter();
counter.increment();
expect(counter._count).toBe(1); // Testeando propiedad privada
});
// Bueno: testea comportamiento público
test('incrementa contador', () => {
const counter = new Counter();
counter.increment();
expect(counter.getCount()).toBe(1); // Testeando método público
});
IA en Jest Testing
Las herramientas de IA pueden acelerar la escritura de tests cuando se usan apropiadamente.
Lo que la IA hace bien:
- Generar casos de test desde firmas de función
- Crear datos mock con formas específicas
- Escribir boilerplate para patrones comunes
- Sugerir edge cases que podrías olvidar
Lo que aún necesita humanos:
- Decidir qué vale la pena testear
- Verificar que los tests realmente testean lo correcto
- Escribir tests para lógica de negocio compleja
- Debuggear tests fallidos
FAQ
¿Para qué se usa Jest?
Jest es un framework de testing JavaScript para unit tests, integration tests y snapshot testing. Provee test runner, librería de assertions, utilidades de mocking y cobertura de código — todo en un paquete. Jest funciona con React, Vue, Angular, Node.js y cualquier proyecto JavaScript o TypeScript.
¿Jest es solo para React?
No. Aunque Jest fue creado por Facebook y es popular en proyectos React, funciona con cualquier codebase JavaScript o TypeScript. Jest testea código backend Node.js, componentes Vue, aplicaciones Angular y JavaScript vanilla igualmente bien. React Testing Library es un paquete separado que complementa Jest para testing específico de React.
¿Cuál es la diferencia entre Jest y Mocha?
Jest es un framework todo-en-uno con assertions (expect), mocking (jest.fn) y cobertura incluidos. Mocha es un test runner que requiere librerías separadas: Chai para assertions, Sinon para mocking, nyc para cobertura. Jest es más fácil de configurar; Mocha ofrece más flexibilidad y personalización. Para proyectos nuevos, Jest suele ser la opción más simple.
¿Cómo mockear llamadas API en Jest?
Varios enfoques funcionan:
- jest.mock() — mockear todo el módulo fetch/axios
- jest.spyOn() — espiar y mockear métodos específicos
- Manual mocks — crear carpeta
__mocks__con implementaciones mock - MSW (Mock Service Worker) — interceptar requests de red para mocking realista de API
¿Cómo configurar Jest con TypeScript?
Instala ts-jest y @types/jest, luego ejecuta npx ts-jest config:init para generar un archivo de configuración. Esto crea un jest.config.js con el preset de ts-jest. Para transformación más rápida en codebases grandes (500+ tests), usa @swc/jest en su lugar — es 20-70x más rápido que ts-jest porque omite la verificación de tipos durante la ejecución de tests. Según la encuesta State of JS 2024, la adopción de TypeScript en proyectos Jest creció al 68% de los encuestados en 2024, convirtiendo la configuración de TypeScript en la pregunta de setup más frecuente.
¿Qué es snapshot testing en Jest?
Snapshot testing captura el output renderizado de un componente (o cualquier valor serializable) y lo guarda en un archivo .snap. En ejecuciones posteriores, Jest compara el output actual con el snapshot guardado y falla si difieren. Es útil para detectar cambios no intencionales en componentes React, respuestas de API u objetos de configuración. Actualiza snapshots con npm test -- -u cuando los cambios son intencionales.
Ver También
- Jest & Testing Library para React - Testing de componentes React con Testing Library
- Jest vs Mocha Comparación - Comparación detallada con benchmarks
- Mocha y Chai - Stack alternativo de testing JavaScript
- Tutorial de Mocha - Guía comprensiva de Mocha + Chai
- React Native Testing Library - Testing móvil con Jest
- Tutorial de Automatización de Tests - Fundamentos de testing más amplios
- Pirámide de Automatización de Testing - Dónde encajan los unit tests en tu estrategia
- Cypress Tutorial - E2E testing para aplicaciones JavaScript
- Playwright vs Cypress - Comparación de frameworks E2E
- pytest Tutorial - Testing Python si trabajas entre lenguajes
