DevPath · Aprende a programar ESPTEN

Node.js, npm y testing

Testing asíncrono y dobles de prueba (mocks)

Probar código asíncrono

Mucho código real es asíncrono: pide datos a un servidor, lee un archivo, espera un temporizador... Devuelve promesas. El reto al probarlo es que el resultado no está disponible de inmediato, así que el test debe esperar.

Hay dos formas de hacerlo bien, y una forma de hacerlo mal.

1. Marcar el test como async y usar await

async function obtenerUsuario(id) {
  return { id, nombre: "Ada" };  // en la vida real haría un fetch
}

test("obtenerUsuario devuelve el nombre", async () => {
  const usuario = await obtenerUsuario(1);
  expect(usuario.nombre).toBe("Ada");
});

2. Devolver la promesa desde el test

Si devuelves la promesa, el runner espera a que se resuelva por ti:

test("obtenerUsuario resuelve", () => {
  return obtenerUsuario(1).then((u) => {
    expect(u.nombre).toBe("Ada");
  });
});

El error clásico: olvidar el await (o el return). El test termina antes de que la promesa se resuelva y la aserción nunca se comprueba: el test "pasa" aunque el código esté roto. Si pruebas async, siempre espera.

Dobles de prueba: mocks y spies

A veces no quieres ejecutar una dependencia real en un test: enviar un email, cobrar una tarjeta o llamar a una API son lentos, costosos o tienen efectos que no queremos en pruebas. La solución son los dobles de prueba: objetos falsos que sustituyen a la dependencia real.

La idea central de un spy es guardar cada llamada para inspeccionarla después:

function espiar(fn) {
  const espia = (...args) => {
    espia.llamadas.push(args);   // registra los argumentos
    return fn(...args);          // delega en la función real
  };
  espia.llamadas = [];
  return espia;
}

const sumarEspiado = espiar((a, b) => a + b);
sumarEspiado(2, 3);
sumarEspiado(10, 1);
console.log(sumarEspiado.llamadas);        // [[2, 3], [10, 1]]
console.log(sumarEspiado.llamadas.length); // 2

Con esto, en un test puedes afirmar cosas como "el botón de pago llamó a cobrar exactamente una vez con el importe correcto", sin cobrar de verdad. Herramientas como Jest o Vitest traen esto integrado (vi.fn(), jest.fn()), pero por dentro hacen justo lo que ves arriba.

Ejemplos

Un spy que cuenta llamadas y registra argumentos

function espiar(fn) {
  const espia = (...args) => {
    espia.llamadas.push(args);
    return fn(...args);
  };
  espia.llamadas = [];
  return espia;
}

const saludo = espiar((nombre) => "Hola, " + nombre);
saludo("Ada");
saludo("Linus");
console.log("Devuelve:", saludo("Grace"));
console.log("Veces llamado:", saludo.llamadas.length);
console.log("Llamadas:", JSON.stringify(saludo.llamadas));
Pon esto en práctica

DevPath es un curso práctico: aquí lees la teoría; en la app la pones en práctica con ejercicios que se ejecutan de verdad, sin conexión.

Empezar gratis en la app →
← Calidad y empaquetadoVer el módulo →