La Pirámide de Pruebas y Dobles de Prueba (Test Doubles)
Para que un equipo pueda desplegar software limpio con alta frecuencia y confianza, la estrategia de testing debe ser sostenible, rápida y rentable.
Construir una suite de pruebas requiere equilibrar los diferentes niveles de la Pirámide de Pruebas y dominar el uso de los Dobles de Prueba (Test Doubles) para aislar los componentes bajo prueba (System Under Test - SUT).
1. La Pirámide de Pruebas (Mike Cohn) #
1/ \ 2 / E2E \ ◄─── 5-10% (Lentas, costosas, frágiles ante cambios de UI) 3 /───────\ 4 / \ 5 / Integración\ ◄─── 15-20% (Prueban adaptadores de BD, APIs y colas) 6 /───────────────\ 7 / \ 8 / Unitarias \ ◄─── 70-80% (Ultrarrápidas, aisladas, ejecutan en milisegundos) 9 /─────────────────────\
- Pruebas Unitarias: Verifican una unidad lógica aislada (clase, función o entidad). Deben ejecutarse miles de ellas en pocos segundos.
- Pruebas de Integración: Comprueban cómo interactúa tu código con componentes externos reales (PostgreSQL, Redis, pasarelas de pago).
- Pruebas End-to-End (E2E): Simulan el flujo completo del usuario a través del navegador o la API de principio a fin.
- Antipatrón del Cono de Helado (Ice Cream Cone): Ocurre cuando un equipo tiene pocos tests unitarios y depende excesivamente de tests E2E lentos que tardan horas en ejecutarse y fallan aleatoriamente por problemas de red (Flaky Tests).
2. Taxonomía de Dobles de Prueba (Gerard Meszaros) #
En testing unitario no queremos interactuar con servicios reales lentos o no deterministas. El término genérico Test Double (el "doble de riesgo" del código) se divide en 5 tipos específicos:
| Tipo de Doble | Propósito y Comportamiento |
|---|---|
| Dummy | Objeto de relleno que se pasa como parámetro pero nunca se utiliza ni se invoca (sirve solo para satisfacer la firma de un constructor). |
| Stub | Proporciona respuestas predefinidas y fijas a las llamadas realizadas durante la prueba (ej. when(repo.buscar(1)).thenReturn(usuarioPrueba)). |
| Spy | Un "espía" que envuelve a un objeto y registra métricas sobre cómo fue llamado (cuántas veces se invocó, con qué argumentos). |
| Mock | Objeto preprogramado con expectativas de interacción que fallará el test si no se llama en el orden o con los parámetros esperados. |
| Fake | Una implementación funcional real pero simplificada, no apta para producción (ej. una base de datos en memoria InMemoryUserRepository basada en un Map). |
3. Ejemplo Práctico de Dobles de Prueba #
Cargando actividad al acercarte…
4. Los Principios F.I.R.S.T. del Código Limpio de Pruebas #
- Fast (Rápidas): Deben ejecutarse en milisegundos para poder lanzarse en cada guardado de archivo.
- Independent (Independientes): Ningún test debe depender del resultado o estado dejado por otro test.
- Repeatable (Repetibles): Deben dar el mismo resultado en cualquier máquina, sin conexión a internet y a cualquier hora.
- Self-Validating (Autovalidables): El resultado es un booleano claro (verde o rojo); no requiere inspeccionar logs manualmente.
- Timely (Oportunas): Escritas en el momento adecuado (en TDD, justo antes del código de producción).
Resumen del tema
Conceptos clave #
- Pirámide de Testing: la gran mayoría de pruebas deben ser unitarias rápidas; las de integración y E2E deben reservarse para verificar cables y flujos críticos.
- Diferencia entre Mocks y Fakes: los Mocks verifican comportamiento e interacciones; los Fakes son implementaciones ligeras en memoria que sustituyen dependencias complejas.
- Principios FIRST: directrices fundamentales que garantizan que la suite de pruebas sea una ayuda para el desarrollo y no un lastre de mantenimiento.
Qué debes recordar #
Prefiere Fakes y Stubs frente al abuso de Mocks complejos para evitar que tus pruebas queden excesivamente acopladas a la estructura interna del código.