Qué son los mocks: para qué sirven y cómo usarlos en tests

Qué son los mocks: para qué sirven y cómo usarlos en tests
En este artículo
  1. Por qué hacen falta
  2. Los cinco tipos de objeto de prueba
  3. Ejemplo con código
  4. Probar también los fallos
  5. Herramientas por lenguaje
  6. Cuándo usarlos y cuándo no
  7. Los cuatro errores típicos
  8. Mocks e inyección de dependencias
  9. Preguntas frecuentes

Un mock es un objeto falso que sustituye a una dependencia real durante una prueba. Imita el comportamiento del original, devuelve lo que tú decides y registra cómo se le ha llamado, para que puedas comprobarlo después.

Se usan sobre todo en pruebas unitarias, y su motivo de existir es simple: aislar el código que quieres probar de todo lo demás.

Por qué hacen falta

Imagina que quieres probar una función que registra usuarios. Esa función guarda en la base de datos, envía un correo de bienvenida y llama a un servicio externo de facturación.

Si la pruebas tal cual, aparecen cuatro problemas:

  • Es lenta. Cada prueba abre conexiones reales. Mil pruebas tardarían horas.
  • Tiene efectos reales. Envía correos de verdad y crea registros de verdad.
  • Es frágil. Si el servicio externo está caído, tu prueba falla aunque tu código esté bien.
  • No aísla el fallo. Si la prueba falla, no sabes si el error está en tu función, en la base de datos o en el correo.

Con mocks sustituyes esas tres dependencias por objetos falsos. La prueba se ejecuta en milisegundos, no manda correos y, si falla, sabes que el problema está en tu función y en ningún otro sitio.

Infografía de los cinco tipos de dobles de prueba: dummy, stub, spy, mock y fake
Los dobles de prueba y para qué sirve cada uno.

Los cinco tipos de objeto de prueba

"Mock" se usa coloquialmente para todo, pero hay una clasificación que conviene conocer porque en entrevistas técnicas se pregunta:

TipoQué haceCuándo se usa
DummySe pasa para rellenar, nunca se usaCuando un parámetro es obligatorio pero irrelevante
StubDevuelve respuestas fijasCuando solo necesitas un dato de entrada controlado
SpyComo el real, pero registra las llamadasCuando quieres comprobar que algo se llamó
MockEspera llamadas concretas y falla si no ocurrenCuando el comportamiento esperado es parte de la prueba
FakeImplementación real pero simplificadaBase de datos en memoria en lugar de la real

La diferencia práctica entre stub y mock es la que más importa: un stub responde a la pregunta "¿qué devuelve?", y un mock a la pregunta "¿se llamó como debía?".

Ejemplo con código

Función que queremos probar:

function registrarUsuario(datos, baseDatos, servicioCorreo) {
    if (!datos.email) {
        throw new Error("El email es obligatorio");
    }
    const usuario = baseDatos.guardar(datos);
    servicioCorreo.enviarBienvenida(usuario.email);
    return usuario;
}

Prueba con mocks:

test("registra el usuario y envía la bienvenida", () => {
    // Creamos los dobles
    const baseDatosFalsa = {
        guardar: jest.fn(datos => ({ id: 1, ...datos }))
    };
    const correoFalso = {
        enviarBienvenida: jest.fn()
    };

    const resultado = registrarUsuario(
        { email: "ana@test.com", nombre: "Ana" },
        baseDatosFalsa,
        correoFalso
    );

    // Comprobamos el resultado
    expect(resultado.id).toBe(1);

    // Y comprobamos que se llamó a las dependencias como esperábamos
    expect(baseDatosFalsa.guardar).toHaveBeenCalledTimes(1);
    expect(correoFalso.enviarBienvenida).toHaveBeenCalledWith("ana@test.com");
});

La prueba tarda milisegundos, no toca la base de datos y no envía ningún correo. Y si mañana alguien borra por error la línea del correo de bienvenida, esta prueba lo detecta.

Probar también los fallos

Una de las mayores ventajas de los mocks es que puedes provocar situaciones difíciles de reproducir en la realidad:

test("si la base de datos falla, no se envía el correo", () => {
    const baseDatosFalsa = {
        guardar: jest.fn(() => { throw new Error("Sin conexión"); })
    };
    const correoFalso = { enviarBienvenida: jest.fn() };

    expect(() => registrarUsuario({ email: "a@b.c" }, baseDatosFalsa, correoFalso))
        .toThrow("Sin conexión");

    expect(correoFalso.enviarBienvenida).not.toHaveBeenCalled();
});

Simular que la base de datos está caída, que la red va lenta o que un servicio devuelve un error 500 sería complicadísimo con dependencias reales. Con un mock es una línea.

Herramientas por lenguaje

LenguajeHerramientas habituales
JavaScript / TypeScriptJest, Vitest, Sinon
Pythonunittest.mock, pytest-mock
JavaMockito, EasyMock
C#Moq, NSubstitute
PHPPHPUnit, Mockery
Gogomock, testify

Cuándo usarlos y cuándo no

Sí conviene

  • Llamadas a APIs externas o servicios de terceros.
  • Bases de datos, en pruebas unitarias.
  • Envío de correos, notificaciones o pagos.
  • Lectura y escritura de archivos.
  • Fechas y horas, para que las pruebas no dependan de cuándo se ejecutan.
  • Generación de números aleatorios.

Mejor evitarlos

  • Con objetos simples. Si la dependencia es una clase sin efectos externos, úsala directa.
  • En pruebas de integración. Ahí el objetivo es justamente comprobar que las piezas encajan de verdad.
  • Para probar código de terceros. No es tu trabajo probar una librería ajena.

Los cuatro errores típicos

1. Simularlo todo. Si una prueba tiene ocho mocks, ya no estás probando tu código: estás probando tus mocks. Suele ser señal de que la función hace demasiadas cosas y habría que dividirla.

2. Mocks que no reflejan la realidad. Si tu mock devuelve un formato de datos que el servicio real ya no usa, la prueba pasa en verde y el sistema falla en producción. Los mocks envejecen en silencio y nadie se entera hasta que es tarde.

3. Probar la implementación en vez del comportamiento. Si compruebas el orden exacto de cinco llamadas internas, cualquier refactorización rompe la prueba aunque el resultado siga siendo correcto. Prueba qué hace el código, no cómo lo hace por dentro.

4. Creer que basta con las unitarias. Con mocks compruebas que cada pieza funciona aislada. Necesitas además pruebas de integración que verifiquen que encajan entre sí.

Mocks e inyección de dependencias

Aquí hay una conexión que vale la pena entender: si tu código es difícil de simular, probablemente está mal diseñado.

// Difícil de probar: crea la dependencia dentro
function registrar(datos) {
    const bd = new BaseDatos();   // no se puede sustituir
    return bd.guardar(datos);
}

// Fácil de probar: la recibe desde fuera
function registrar(datos, bd) {
    return bd.guardar(datos);
}

Eso es inyección de dependencias. La testabilidad no es un requisito añadido: es un indicador de que las responsabilidades están bien separadas.

Preguntas frecuentes

¿Mock y stub son lo mismo?
No. El stub devuelve datos fijos; el mock además verifica que se le llamó como esperabas. En la práctica muchas herramientas los mezclan.

¿Los mocks sustituyen a las pruebas de integración?
No. Son complementarios. Los mocks prueban unidades aisladas; la integración prueba que el conjunto funciona.

¿Cuántos mocks son demasiados?
No hay un número exacto, pero si pasas de tres o cuatro en una sola prueba, revisa si la función tiene demasiadas responsabilidades.

¿Se pueden simular fechas?
Sí, y es muy recomendable. Una prueba que depende de la fecha real fallará algún día sin que nadie haya tocado nada.


¿Aprendiendo desarrollo? En nuestro catálogo de cursos técnicos tienes formación desde cero, con acceso inmediato y de por vida.