Un código sin pruebas está defectuoso por diseño. Las pruebas te otorgan la seguridad necesaria para refactorizar, lanzar nuevas funcionalidades con agilidad y dormir tranquilo sabiendo que un despliegue a medianoche no causará caídas en producción. Esta guía te equipará con todo lo esencial para iniciar tus pruebas de JavaScript de manera efectiva.
Tipos de Pruebas Esenciales
- Pruebas Unitarias (Unit Tests): Enfocadas en verificar unidades de código aisladas, como funciones o módulos individuales. Son rápidas, económicas de implementar y fundamentales para tener una base sólida.
- Pruebas de Integración (Integration Tests): Verifican cómo interactúan múltiples unidades de código. Son cruciales para validar flujos como las llamadas a APIs, consultas a bases de datos o la comunicación entre componentes.
- Pruebas End-to-End (E2E Tests): Simulan la experiencia completa del usuario, interactuando con la aplicación tal como lo haría una persona: haciendo clic en botones, completando formularios y navegando por las páginas.
La Pirámide de las Pruebas
/ Pruebas E2E \ ← Pocas (lentas, costosas)
/ Pruebas Integración \ ← Moderadas
/ Pruebas Unitarias \ ← Muchas (rápidas, económicas)
Comenzando con Jest
Configuración
# Instalar Jest
npm install -D jest
# Para TypeScript
npm install -D jest ts-jest @types/jest
# Añadir script de prueba en package.json
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
}
Escribiendo Tu Primera Prueba
math.js
export function sumar(a, b) {
return a + b;
}
export function dividir(a, b) {
if (b === 0) throw new Error('No se puede dividir por cero');
return a / b;
}
math.test.js
import { sumar, dividir } from './math';
describe('sumar', () => {
test('suma dos números positivos', () => {
expect(sumar(2, 3)).toBe(5);
});
test('maneja números negativos', () => {
expect(sumar(-1, -1)).toBe(-2);
});
test('maneja el cero', () => {
expect(sumar(0, 5)).toBe(5);
});
});
describe('dividir', () => {
test('divide dos números', () => {
expect(dividir(10, 2)).toBe(5);
});
test('lanza error al dividir por cero', () => {
expect(() => dividir(10, 0)).toThrow('No se puede dividir por cero');
});
});
Matchers Comunes en Jest
Matchers de Jest
// Igualdad
expect(valor).toBe(42); // Igualdad estricta (===)
expect(obj).toEqual({ a: 1, b: 2 }); // Igualdad profunda
// Veracidad
expect(valor).toBeTruthy();
expect(valor).toBeFalsy();
expect(valor).toBeNull();
expect(valor).toBeUndefined();
expect(valor).toBeDefined();
// Números
expect(valor).toBeGreaterThan(3);
expect(valor).toBeLessThanOrEqual(10);
expect(0.1 + 0.2).toBeCloseTo(0.3); // Números de punto flotante
// Cadenas de texto
expect(str).toMatch(/regex/);
expect(str).toContain('subcadena');
// Arrays
expect(arr).toContain('elemento');
expect(arr).toHaveLength(3);
// Objetos
expect(obj).toHaveProperty('nombre');
expect(obj).toMatchObject({ nombre: 'Alicia' });
Mocking de Dependencias
Mock de Funciones y Módulos
// Mock de una función
const mockFn = jest.fn();
mockFn.mockReturnValue(42);
mockFn.mockResolvedValue({ id: 1 }); // Para asíncronas
// Verificar llamadas
expect(mockFn).toHaveBeenCalled();
expect(mockFn).toHaveBeenCalledWith('arg1', 'arg2');
expect(mockFn).toHaveBeenCalledTimes(3);
// Mock de un módulo
jest.mock('./database', () => ({
obtenerUsuario: jest.fn().mockResolvedValue({ id: 1, nombre: 'Alicia' }),
guardarUsuario: jest.fn().mockResolvedValue(true),
}));
Probando Código Asíncrono
Pruebas Asíncronas
// async/await (recomendado)
test('obtiene datos de usuario', async () => {
const user = await obtenerUsuario(1);
expect(user.nombre).toBe('Alicia');
});
// Probando promesas rechazadas
test('lanza error para ID inválido', async () => {
await expect(obtenerUsuario(-1)).rejects.toThrow('ID inválido');
});
Desarrollo Guiado por Pruebas (TDD)
- Rojo (Red): Escribe una prueba que falle intencionalmente.
- Verde (Green): Escribe el código mínimo indispensable para que la prueba pase.
- Refactorizar (Refactor): Mejora el código asegurándote de que las pruebas sigan pasando.
El TDD te obliga a pensar en la interfaz antes de la implementación, resultando en código inherentemente testeable y bien diseñado.
Mejores Prácticas
- Prueba el comportamiento, no la implementación. Los cambios internos no deberían romper las pruebas si el comportamiento externo es el mismo.
- Una aserción por concepto de prueba. Las pruebas deben fallar por una única razón para facilitar la depuración.
- Usa nombres descriptivos para las pruebas.
"debería devolver 404 si el usuario no existe"es más claro que"test getUser". - Aspira a una cobertura de código superior al 80%, pero no persigas el 100% ciegamente, ya que los rendimientos decrecen.
- Ejecuta las pruebas en tu CI. Cada solicitud de integración (pull request) debe pasar todas las pruebas antes de fusionarse.
- Mantén las pruebas rápidas. Las pruebas lentas desalientan su ejecución frecuente. Utiliza mocks para dependencias externas.
Prueba Nuestras Herramientas Gratuitas de JavaScript
Minifica y formatea tus archivos de prueba de JavaScript.