Pirámide de Pruebas
Este artículo es una definición de concepto sobre la pirámide de pruebas, incluyendo preguntas de examen y etiquetas.
En Resumen
La pirámide de pruebas prioriza muchas pruebas unitarias rápidas y estables, menos pruebas de integración y pocos tests end-to-end, para lograr máxima confianza con costos mínimos. El objetivo es obtener retroalimentación rápida en CI y cobertura confiable sin una sobrecarga frágil de pruebas de UI.
Descripción Técnica Compacta
La pirámide de pruebas estructura el aseguramiento de calidad automatizado en capas según costo, tiempo de ejecución y precisión en la localización de errores.
- Capa unitaria: rápida, aislada, determinística; alta localización de defectos; uso de mocks y stubs.
- Capa de integración: interacción real de componentes (base de datos, sistema de archivos, red); a menudo con contract tests entre servicios.
- Capa E2E: pocos, pero flujos críticos para el negocio a través de UI e infraestructura; alto nivel de confianza, costos superiores.
Buenas métricas: code coverage (como tendencia), mutation testing (efectividad), flaky rate (estabilidad). Antipatrones: cono de helado (demasiadas pruebas de UI), reloj de arena (muy pocas pruebas unitarias).
Puntos Clave para el Examen
- Distribución objetivo: aproximadamente 70 % unitarias, 20 % integración, 10 % E2E (depende del contexto): La pirámide distribuye las pruebas según costo y velocidad. Cuanto más cercano al código, más pruebas deberían ejecutarse. Estas cifras son referencias y pueden variar según el proyecto.
- Capa unitaria: rápida, aislada, determinística, alta localización de defectos: Las pruebas unitarias verifican componentes individuales sin dependencias externas. Son la base de la pirámide y proporcionan la retroalimentación más rápida.
- Integración: interfaces reales, gestión de datos de prueba, servicios en contenedores: Las pruebas de integración verifican cómo varios componentes trabajan juntos. Las bases de datos, APIs o brokers de mensajes generalmente son reales o se simulan en contenedores.
- E2E: pocos flujos críticos, entorno real cercano a producción: Las pruebas end-to-end simulan trayectorias completas del usuario. Son costosas y lentas, por lo que deben concentrarse en procesos críticos para el negocio.
- Contract tests para límites de servicios (Microservicios): Los contract tests verifican que se cumple el contrato entre consumidor y proveedor. Son especialmente importantes en sistemas distribuidos con muchos servicios.
- Pipeline CI: etapas de retroalimentación rápida, ejecución paralela, versionamiento de artefactos: La pipeline debe ejecutar primero y rápidamente las pruebas unitarias, antes de iniciar las E2E más costosas. La ejecución paralela y las etapas claras mantienen el tiempo de retroalimentación corto.
- Métricas: coverage, mutation score, tiempo de build, flaky rate: Las métricas ayudan a evaluar la calidad de las pruebas. La cobertura sola no es significativa; el mutation score muestra si las pruebas realmente detectan errores.
- Documentación: estrategia de pruebas, catálogo de casos de prueba, matriz de riesgos: Una estrategia de pruebas clara documenta qué pruebas existen en cada nivel y por qué. Las matrices de riesgos priorizan las áreas con mayor probabilidad de error.
Componentes Clave
- Niveles de pruebas (Unit, Integración, E2E) – Las tres capas de la pirámide difieren en aislamiento, velocidad y costo. Las pruebas unitarias son pequeñas y rápidas, las E2E amplias y realistas. Cada nivel tiene un propósito distinto.
- Test doubles (Mock, Stub, Fake, Spy) – Los test doubles reemplazan dependencias reales en pruebas unitarias. Los mocks verifican interacciones, los stubs proporcionan valores fijos, los fakes contienen lógica simple, los spies registran llamadas.
- Estrategia de datos de prueba (Factories, Fixtures, datos semilla, Reset) – Los datos de prueba consistentes son cruciales para tests estables. Las factories generan datos flexibles, los fixtures proporcionan estados iniciales conocidos, un reset después de cada prueba previene efectos secundarios.
- Infraestructura en pruebas (contenedores de base de datos, message broker, sandbox) – Las pruebas de integración a menudo utilizan contenedores o instancias de prueba para bases de datos y brokers. Esto hace las pruebas más realistas, pero más lentas y exigentes en mantenimiento.
- Contract Testing (dirigido por el consumidor, versionamiento) – Los contract tests verifican contratos de interfaz entre servicios. El consumidor define expectativas, el proveedor asegura que se cumplan, incluso con versionamiento.
- Ejecución de pruebas (etapas CI, gate rápido en capa unitaria) – La pipeline CI ejecuta primero las pruebas unitarias como gate de calidad rápido. Solo cuando estas pasan, se ejecutan las pruebas de integración y finalmente las E2E.
- Estabilidad (eliminación de flaky tests, control de tiempo y aleatoriedad) – Los flaky tests producen resultados no confiables. Mediante tiempo controlado, valores aleatorios determinísticos e aislamiento limpio, pueden reducirse o eliminarse.
- Cobertura y Efectividad (Coverage, Mutation Testing, cobertura de riesgos) – Coverage muestra qué líneas de código se ejecutan. Mutation testing verifica si las pruebas detectan errores reales. Ambos juntos dan una mejor imagen de la calidad de las pruebas.
- E2E Selectivo (rutas críticas, suite smoke, regresión visual conservadora) – No cada ruta necesita una prueba E2E. Los procesos de negocio críticos, pruebas smoke después de deployments y regresión visual dirigida cubren lo esencial.
- Mantenimiento (refactoring, librerías de helpers compartidas, convenciones de nombres) – El código de prueba es tan importante como el código de producción. El refactoring regular, helpers compartidos y convenciones de nombres claras mantienen la base de pruebas mantenible.
Ejemplo Práctico (Servicio de Pedidos)
Capa unitaria:
- Arrange: Product con precio, mock de DiscountPolicy devuelve 10 % de descuento
- Act: OrderService.totalForBasket()
- Assert: cantidad esperada = cantidad calculada (sin accesos a DB)
Capa de integración:
- Arrange: DB real en contenedor, Repository guarda/lee Order
- Act: OrderRepository.save() + OrderRepository.findById()
- Assert: campos guardados idénticos, transacción se revierte
Capa E2E:
- Arrange: iniciar aplicación con automatización de navegador
- Act: usuario añade artículo al carrito, va a checkout, hace clic en "Completar pedido"
- Assert: confirmación de pedido visible, entrada en DB presente, evento en message broker
Ventajas e Inconvenientes
Ventajas
- Tiempos de retroalimentación cortos
- Alta localización de defectos
- Pipeline robusta
- Menores costos de mantenimiento
- Mejor previsibilidad
- Mayor frecuencia de releases
Inconvenientes
- Configuración inicial de infraestructura
- Mantenimiento de test doubles y fixtures
- Posibles puntos ciegos por distribución incorrecta
- E2E sigue siendo frágil
Preguntas Típicas de Examen (con Respuesta Breve)
- ¿Por qué la pirámide de pruebas tiene sentido económico? Muchas pruebas unitarias baratas detectan la mayoría de defectos temprano; las pruebas E2E costosas se limitan a flujos críticos.
- ¿Cómo se reconocen demasiadas pruebas de UI? Builds largos, alta flaky rate, falsos negativos frecuentes, localización de defectos difícil (cono de helado).
- ¿Para qué sirven los contract tests en Microservicios? Estabilizan relaciones de interfaz, verifican compatibilidad independientemente del sistema completo.
- ¿Cómo se previenen los flaky tests? Controlar tiempo y aleatoriedad, mockear/encapsular dependencias externas, aislamiento limpio, datos determinísticos.
- ¿Rol del mutation testing? Verifica si las pruebas son lógicamente efectivas (no solo tocar líneas) → mejor conclusión que cobertura pura.
Estrategia de aprendizaje
- Inicio con comprensión: Compara los tres niveles de prueba usando una funcionalidad concreta (por ejemplo, carrito de compras). Reflexiona sobre qué lógica se prueba en unitarias, qué interfaz en integración y qué flujo de usuario en E2E.
- Método de profundización: Escribe una combinación de pruebas unitarias e integración para código existente. Observa las diferencias en velocidad, mensajes de error y esfuerzo de configuración.
- Entrenamiento enfocado en evaluación: Asigna escenarios al nivel correcto y explica por qué una distribución incorrecta (por ejemplo, cono de helado) es problemática.
- Prevención de errores: Evita tests frágiles desde el inicio: controla el tiempo, la aleatoriedad y las dependencias externas; aísla completamente los tests.
Ejercicio práctico 1: Test unitario para cálculo de descuento
Un carrito contiene productos con precios. Una DiscountPolicy calcula un descuento del 10 %. En el test unitario, la política se prueba aislada: para un precio de entrada de 100 €, el descuento debe ser 10 €. No se necesitan servicios externos ni bases de datos.
Ejercicio práctico 2: Test de integración para un repositorio
Un OrderRepository almacena y lee pedidos desde una base de datos real en un contenedor. El test verifica que los campos guardados se recuperen correctamente y que las transacciones se revierten adecuadamente. Es más lento que un test unitario, pero más realista.
Ejercicio práctico 3: Entender Mutation Testing
Un test tiene 100 % de cobertura, pero una herramienta de Mutation Testing cambia una condición en el código y el test no falla. Esto demuestra que el test no verifica la lógica realmente. El Mutation Testing expone tales brechas.
Tarea práctica 1: Determinar el nivel de prueba
Quieres verificar que el cálculo del impuesto para un artículo sea correcto. ¿En qué nivel debería estar el test?
Solución: En el nivel unitario, porque el cálculo de impuestos es una lógica aislada sin dependencias externas.
Tarea práctica 2: Evaluar la distribución
Un proyecto tiene 500 tests E2E, pero solo 50 tests unitarios. Las compilaciones tardan mucho y a menudo fallan de forma poco confiable. ¿Cuál es el problema?
Solución: El proyecto forma un cono de helado: demasiados tests de UI, muy pocos tests unitarios. La solución es una distribución más fuerte hacia tests unitarios y tests E2E selectivos.
Tarea práctica 3: Analizar un test frágil
Un test falla ocasionalmente porque accede a la hora actual. ¿Cómo se puede solucionar?
Solución: La hora debe estar controlada en el test, por ejemplo mediante un Testdouble para la fuente de tiempo o mediante valores fijos. Esto hace que el test sea determinista.
Análisis temático
- Núcleo técnico: Niveles de prueba y su interacción. La pirámide define claramente qué tests se ejecutan en qué nivel. Los tests unitarios aseguran la lógica, los tests de integración el trabajo conjunto, los tests E2E los flujos de usuario críticos.
- Desafíos de implementación: Equilibrio entre velocidad y realismo. Demasiados tests E2E ralentizan la pipeline, muy pocos tests de integración pasan por alto errores de interfaz. La distribución correcta es específica del proyecto.
- Implicaciones de seguridad: Mutation Testing y cobertura para rutas críticas. Especialmente con código sensible a la seguridad, la cobertura no es suficiente. El Mutation Testing verifica si los tests reconocen realmente variantes defectuosas.
- Obligaciones de documentación: Estrategia de prueba y catálogo de tests como documentación del proyecto. Una estrategia de prueba documentada explica qué tests existen en qué nivel y qué riesgos cubren.
- Evaluación económica: Esfuerzo de prueba frente a costos de errores y velocidad de lanzamiento. Las buenas pruebas reducen errores costosos en producción y permiten lanzamientos más rápidos. Las malas pruebas cuestan por mantenimiento y fragilidad.
Fuentes más importantes
- https://martinfowler.com/articles/practical-test-pyramid.html
- https://testing.googleblog.com
- https://pact.io



