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) Tiempo de lectura: 15 minutos
Tu suite de tests tarda 10 minutos. La mitad de los tests son flaky. Nadie confía en los resultados.
Jest cambia esto. Es rápido, confiable y funciona de caja. Sin infierno de configuración. Sin armar cinco librerías diferentes.
Este tutorial enseña Jest desde cero — matchers, mocking, testing async y los patrones que hacen tests mantenibles.
¿Qué es Jest?
Jest es un framework de testing JavaScript creado por Facebook. Ejecuta tus tests, provee assertions, mockea dependencias y genera reportes de cobertura — todo en un paquete.
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.
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.
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
});
Testing Jest con Asistencia de IA
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
Recursos Oficiales
Ver También
- Jest & Testing Library para React - Testing de componentes React con Testing Library
- Mocha y Chai - Stack alternativo de testing JavaScript
- Pirámide de Automatización de Testing - Dónde encajan los unit tests en tu estrategia
- Cypress Tutorial - E2E testing para aplicaciones JavaScript
