Unit Tests con Jest: qué probar y cómo escribir pruebas útiles

Qué es una prueba unitaria

Una prueba unitaria verifica el comportamiento de una unidad de código, normalmente una función o una clase, en aislamiento. La idea es darle una entrada conocida, ejecutar la unidad y comparar el resultado con lo esperado.

function sumar(a, b) {
  return a + b;
}

test('suma dos números', () => {
  expect(sumar(2, 3)).toBe(5);
});

La prueba corre rápido, no necesita red ni navegador, y documenta qué debe hacer la unidad con cada conjunto de entradas.

La estructura básica de Jest

Jest ofrece funciones globales para describir y ejecutar pruebas. La estructura típica separa qué se quiere probar, el caso concreto y la comprobación.

describe('sumar', () => {
  test('suma valores positivos', () => {
    expect(sumar(1, 2)).toBe(3);
  });

  test('suma valores negativos', () => {
    expect(sumar(-1, -2)).toBe(-3);
  });
});

describe agrupa casos relacionados y convierte la salida en una lista legible de comportamiento esperado.

Qué matchers usar

Jest incluye matchers para distintos tipos de resultado. Los más habituales:

  • toBe para valores exactos.
  • toEqual para objetos y arreglos.
  • toHaveLength para tamaños.
  • toThrow para funciones que lanzan errores.
  • toContain para revisar elementos dentro de una colección.
test('construye el usuario correctamente', () => {
  const usuario = crearUsuario({ nombre: 'Ana', edad: 30 });
  expect(usuario).toEqual({ nombre: 'Ana', edad: 30 });
});

test('rechaza edades inválidas', () => {
  expect(() => validarEdad(-1)).toThrow('Edad inválida');
});

Elegir el matcher correcto hace que un fallo describa el problema exacto en lugar de un booleano genérico.

Aislar dependencias con mocks

Una unidad no siempre es pura. Puede llamar a una API, leer el reloj o consultar una base de datos. En una prueba unitaria esas dependencias se reemplazan por versiones simuladas que devuelven valores controlados.

jest.mock('./servicioUsuarios');

test('carga y transforma usuarios', async () => {
  servicioUsuarios.obtener.mockResolvedValue([
    { nombre: 'Luis', edad: 25 },
    { nombre: 'María', edad: 30 }
  ]);

  const resultado = await cargarUsuarios();

  expect(resultado).toHaveLength(2);
  expect(servicioUsuarios.obtener).toHaveBeenCalledTimes(1);
});

Con mockResolvedValue se controla la respuesta, y con toHaveBeenCalledTimes se verifica que la unidad interactuó con su dependencia de la forma esperada.

Lo que no debe ir en una prueba unitaria

Una prueba unitaria pierde sentido cuando:

  • Depende de la red o de un servidor real.
  • Usa temporizadores que hacen el resultado impredecible.
  • Prueba varias unidades al mismo tiempo y dificulta saber qué falló.
  • Repite la implementación en lugar de verificar el comportamiento.

Si una prueba depende de una base de datos concreta o de movimientos del usuario, quizá ya no es unitaria.

Cobertura como señal, no como meta

Jest puede generar reportes de cobertura sobre cuántas líneas, ramas y funciones se ejecutaron.

jest --coverage

La cobertura muestra qué código quedó sin probar, pero un porcentaje alto no garantiza buenas pruebas. Más útil que perseguir el cien por ciento es cubrir los caminos críticos: validaciones, condiciones límite, errores y casos de borde.

Consideraciones

Las pruebas unitarias no reemplazan a las pruebas de integración ni de extremo a extremo, pero son la primera red de seguridad: rápidas, deterministas y ejecutables en cada cambio. Con Jest, la clave está en probar comportamiento, aislar dependencias y elegir matchers que hagan los fallos fáciles de leer.

© Arturo Juárez Monroy