JavaScript de Cero a Experto / Testing con Vitest
Mocks y spies: dobles de acción para tus tests
Testear funciones puras es fácil: entra X, sale Y. Pero el código real habla con el mundo —pide datos a un servidor, lee la hora, guarda en el navegador— y eso es lento, impredecible y a veces imposible de repetir. La solución son los mocks: dobles de acción que sustituyen esas dependencias por versiones falsas y controladas. Y aquí cobra sentido, por fin, el "bus de mentiras" del módulo 13.
El doble de riesgo del cine
vi.fn(): una función espía
Vitest trae vi.fn(), que crea una función falsa que además ESPÍA: recuerda
cuántas veces la llamaron y con qué argumentos. Perfecta para verificar que tu
código llamó a algo correctamente:
import { it, expect, vi } from "vitest";
it("notifica al observador cuando se agrega un gasto", () => {
const observador = vi.fn(); // un espía: función falsa que registra
const bolsillo = crearBolsillo();
bolsillo.suscribir(observador); // le pasamos el espía
bolsillo.agregar("Café", 4000);
// ahora interrogamos al espía:
expect(observador).toHaveBeenCalled(); // ¿se llamó?
expect(observador).toHaveBeenCalledTimes(1); // ¿una vez?
expect(observador).toHaveBeenCalledWith({ nombre: "Café", valor: 4000 }); // ¿con qué?
});El espía no HACE nada (devuelve undefined por defecto), pero lo recuerda
todo. Así verificas COMPORTAMIENTO invisible: que tu bolsillo realmente avisó a
sus observadores, con los datos correctos —justo el pub-sub del módulo 13,
ahora auditado por un test.
Devolver valores controlados
Un mock también puede fingir devolver datos. mockReturnValue para valores
síncronos, mockResolvedValue para promesas (una red falsa):
import { it, expect, vi } from "vitest";
it("procesa los gastos que devuelve el repositorio", async () => {
// un repositorio FALSO que devuelve datos fijos, sin tocar la red real
const repoFalso = {
obtenerGastos: vi.fn().mockResolvedValue([
{ nombre: "Bus", valor: 2800 },
{ nombre: "Almuerzo", valor: 18000 },
]),
};
const total = await calcularTotalDesde(repoFalso);
expect(total).toBe(20800);
expect(repoFalso.obtenerGastos).toHaveBeenCalledTimes(1);
});calcularTotalDesde cree que habló con un repositorio real; en verdad habló
con un doble que devolvió datos que TÚ elegiste. El test corre en un
milisegundo, siempre da igual, y no necesita internet. Eso es aislar la
unidad bajo prueba de sus dependencias.
El pago del módulo 13: inyección = mocking gratis
Controlar el tiempo y el azar
Las dos impurezas del módulo 12 —Date.now() y Math.random()— vuelven
determinista un test con las herramientas de Vitest:
import { it, expect, vi, afterEach } from "vitest";
it("marca el gasto con la fecha actual", () => {
// congelamos el reloj en una fecha fija
vi.setSystemTime(new Date("2026-07-20T10:00:00"));
const gasto = crearGasto("Café", 4000);
expect(gasto.fecha).toBe("2026-07-20T10:00:00.000Z");
vi.useRealTimers(); // devolvemos el reloj real al terminar
});
it("elige una opción con azar controlado", () => {
// forzamos Math.random a devolver siempre 0.5
vi.spyOn(Math, "random").mockReturnValue(0.5);
expect(dadoDe(6)).toBe(4); // ahora el 'azar' es predecible
});Con el reloj congelado, un test de "marca la fecha de hoy" deja de fallar
mañana. Con Math.random fijado, puedes probar lógica que dependía del azar.
Este es el momento donde la lección del módulo 12 —empujar las impurezas a los
bordes— paga doble: cuanto más pura sea tu lógica, menos mocking necesitas.
Cuándo NO mockear
Quieres testear una función que envía un email cuando el saldo llega a cero, pero no quieres enviar emails de verdad en cada test. ¿Cuál es el enfoque correcto?
Mini-reto
En tu proyecto con Vitest (de la lección 3): 1) escribe una función crearGasto (nombre, valor) que ponga fecha: new Date().toISOString(); 2) testéala
congelando el tiempo con vi.setSystemTime a una fecha fija y verificando la
fecha exacta; 3) escribe una función que reciba un logger como parámetro,
pásale un vi.fn() en el test, y verifica con toHaveBeenCalledWith que se
registró el mensaje correcto. Siente cómo recibir el logger lo hizo trivial.
Qué sigue
Tienes el arsenal completo del testing: por qué, el framework por dentro, Vitest, buenos tests, TDD y mocks. Hora de juntarlo todo: el reto del módulo te pone a escribir una suite de tests de verdad para el motor de "Mi Bolsillo" —casos límite, un error que debe lanzarse, e inmutabilidad verificada. Tu primera red de seguridad real.