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.