Skip to content
IRC-CodingIRC-Coding
Test PyramidUnit TestIntegration TestEnd to End TestMutation TestingFlaky TestContract TestCI

Test Pyramid: Unit, Integration, E2E & Mutation Testing

Aprende la pirámide de testing: muchos Unit Tests rápidos, menos Integration Tests y pocos E2E Tests. Mutation Testing, Flaky Tests y métricas.

S

schutzgeist

12 min read
Test Pyramid: Unit, Integration, E2E & Mutation Testing

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

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
  7. 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.
  8. 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.
  9. 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.
  10. 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)

  1. ¿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.
  2. ¿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).
  3. ¿Para qué sirven los contract tests en Microservicios? Estabilizan relaciones de interfaz, verifican compatibilidad independientemente del sistema completo.
  4. ¿Cómo se previenen los flaky tests? Controlar tiempo y aleatoriedad, mockear/encapsular dependencias externas, aislamiento limpio, datos determinísticos.
  5. ¿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

  1. 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.
  2. 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.
  3. 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.
  4. 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

  1. https://martinfowler.com/articles/practical-test-pyramid.html
  2. https://testing.googleblog.com
  3. https://pact.io

FAQ: Pirámide de pruebas, Unitarias, Integración, E2E, Mutation Testing y Tests frágiles

1. ¿Qué es la pirámide de pruebas?

La pirámide de pruebas es un modelo para distribuir tests automatizados. Recomienda muchos tests unitarios, menos tests de integración y solo algunos tests E2E, para lograr retroalimentación rápida a bajo costo.

2. ¿Qué es un test unitario?

Un test unitario verifica una unidad pequeña e aislada de código, típicamente una función o método. Es rápido, determinista y proporciona localización precisa de errores.

3. ¿Qué es un test de integración?

Un test de integración verifica cómo múltiples componentes o sistemas trabajan juntos, por ejemplo entre la aplicación y la base de datos o entre dos servicios. Es más realista que un test unitario, pero más lento.

4. ¿Qué es un test E2E?

Un test end-to-end simula un flujo de usuario completo a través de todas las capas de la aplicación. Proporciona gran confianza, pero es costoso, lento y más propenso a errores.

5. ¿Cuál es la diferencia entre Mock y Stub?

Un Mock verifica interacciones, es decir, si ciertos métodos fueron llamados. Un Stub proporciona respuestas predefinidas y simula una dependencia sin verificar interacciones.

6. ¿Qué es un Fake?

Un Fake es una implementación simple de una dependencia con lógica rudimentaria. A diferencia de un Stub o Mock, un Fake puede funcionar realmente, pero solo de forma simplificada.

7. ¿Qué es un Spy?

Un Spy registra llamadas a una dependencia real o falsificada. Después del test, se puede verificar qué métodos fueron llamados y cuántas veces.

8. ¿Qué es un test frágil?

Un test frágil produce resultados diferentes con el mismo código, a veces exitoso, a veces fallido. Frecuentemente es causado por tiempo, aleatoriedad o dependencias externas.

9. ¿Cómo se previenen los tests frágiles?

Se evitan tests frágiles controlando el tiempo, usando valores aleatorios deterministas, aislando completamente, manteniendo datos de test estables y evitando dependencias externas reales en tests unitarios.

10. ¿Qué es Mutation Testing?

Mutation Testing cambia pequeñas partes del código para verificar si los tests detectan estos cambios. La puntuación de mutación muestra cuán efectivos son realmente los tests.

11. ¿Qué es la puntuación de mutación?

La puntuación de mutación indica qué proporción de cambios de código insertados artificialmente fueron detectados por los tests. Una puntuación alta significa que los tests realmente verifican la lógica.

12. ¿Qué es la cobertura de código?

La cobertura de código muestra qué porcentaje del código se ejecuta mediante tests. Es una métrica útil, pero no una prueba única de calidad, ya que no indica si los tests realmente detectan errores.

13. ¿Qué es un Contract Test?

Un Contract Test verifica el contrato entre dos servicios, típicamente consumidor y proveedor. Asegura que los cambios de interfaz no rompan la compatibilidad.

14. ¿Qué es un cono de helado en la pirámide de pruebas?

Un cono de helado describe una distribución invertida con muchos tests de UI o E2E y pocos tests unitarios. Causa compilaciones largas, fragilidad alta y mala localización de errores.

15. ¿Qué es un reloj de arena en la pirámide de pruebas?

Un reloj de arena describe una distribución con muchos tests unitarios y muchos tests E2E, pero poco en el medio. Faltan tests de integración en gran medida, por lo que los problemas de interfaz se descubren demasiado tarde.

16. ¿Qué es un Smoke Test?

Un Smoke Test es un test rápido y superficial que verifica si las funciones básicas de una aplicación funcionan después de un despliegue. Frecuentemente se ejecuta como un test E2E.

17. ¿Qué es un Testdouble?

Un Testdouble es un sustituto de una dependencia real en un test. Incluye Mocks, Stubs, Fakes y Spies. Permiten tests aislados y rápidos.

18. ¿Qué es una Fixture?

Una Fixture es un conjunto fijo de datos de test preparados antes de un test. Proporciona condiciones reproducibles y simplifica la configuración en múltiples tests.

19. ¿Qué es un Testcontainer?

Un Testcontainer es una instancia ligera de una infraestructura externa, como una base de datos o un Message Broker, iniciada para tests de integración. Permite tests realistas sin sistemas productivos.

20. ¿Qué es testing determinista?

Testing determinista significa que un test con las mismas entradas siempre produce el mismo resultado. El tiempo, la aleatoriedad y los estados externos deben estar controlados para lograr determinismo.

21. ¿Qué es una pipeline de CI?

Una pipeline de CI automatiza la compilación, prueba e inspección de código en cada cambio. Ejecuta tests en fases y proporciona retroalimentación rápida al equipo de desarrollo.

22. ¿Qué es un catálogo de casos de prueba?

Un catálogo de casos de prueba documenta todos los tests existentes con su nivel, propósito y riesgos cubiertos. Ayuda en la planificación, revisión y evolución de la estrategia de prueba.

23. ¿Qué es una matriz de riesgos en testing?

Una matriz de riesgos clasifica áreas del sistema por probabilidad de ocurrencia e impacto de fallos. Ayuda a concentrar esfuerzo de prueba donde el riesgo es más alto.

24. ¿Qué es un anti-patrón en la pirámide de pruebas?

Un anti-patrón es una distribución que contradice la pirámide. El cono de helado y el reloj de arena son ejemplos conocidos. Conducen a tests lentos, inestables o insuficientes.

25. ¿Por qué no es suficiente la cobertura alta por sí sola?

La cobertura alta solo indica que se ejecutaron líneas de código, no que los tests detecten errores. Un test puede tocar código sin verificar su lógica. El Mutation Testing y las aserciones dirigidas mejoran la significancia.
Volver al blog
Share:

Nächster Artikel in Calidad de Software

Weiterlesen
Proceso de Testing: Planificación y Ejecución

Entradas relacionadas