La gran mala interpretación de TDD (2022)
El desarrollo guiado por pruebas (TDD) y las pruebas unitarias se examinan aquí, y muchos argumentan que las pruebas estrechamente acopladas a detalles de implementación crean suites frágiles que dificultan la refactorización y desperdician esfuerzo. Los comentaristas contrastan las pruebas unitarias de bajo nivel con las pruebas de integración y extremo a extremo de mayor nivel, debatiendo dónde está el mejor coste-beneficio, cuánta cobertura es realmente útil y si las pruebas deberían validar principalmente el comportamiento desde una frontera de usuario o de sistema. Debajo hay una preocupación más amplia por las metodologías dogmáticas: algunos ven TDD como una potente ayuda de diseño cuando se aplica con criterio, mientras que otros lo consideran una herramienta situacional que debe elegirse según el contexto, los objetivos de calidad y la complejidad del sistema.
Significado de “unit” en las pruebas unitarias
- La afirmación del artículo de que “unit” se refería originalmente a una prueba aislada está muy discutida.
- Varios comentaristas citan definiciones anteriores (por ejemplo, “módulo pequeño compilable, ~100 LOC”) en las que la unidad es claramente el código bajo prueba, no la prueba.
- El consenso en el hilo: el lenguaje ha derivado, pero la mayoría de los practicantes y documentos históricos tratan la unidad como un componente de código (función, clase, módulo), aunque algunos aceptan “prueba independiente” como una noción moderna útil.
¿TDD trata sobre pruebas o diseño?
- Un bando: la gran mala interpretación es tratar TDD como pruebas; principalmente es una práctica de diseño que usa pruebas para dar forma a las APIs y la separación de responsabilidades.
- Bando opuesto: TDD como diseño está sobrevalorado o es perjudicial; la habilidad y el criterio de diseño importan más, y TDD ofrece una guía de diseño débil, especialmente para desarrolladores menos experimentados.
- Algunos separan el término: Test-Driven Development vs Test-Driven Design, argumentando que a menudo se confunden.
Pruebas unitarias vs integración/E2E
- Muchos sostienen que las pruebas funcionales/de integración de alto nivel aportan más valor:
- Más cercanas al comportamiento visible para el usuario.
- Más estables frente a refactors.
- Evitan sobrerrelacionar las pruebas con la implementación y la proliferación de mocks.
- Otros defienden la pirámide de pruebas tradicional: numerosas pruebas unitarias rápidas y aisladas detectan casos límite y errores que pruebas más amplias pueden pasar por alto, especialmente en algoritmos complejos o bibliotecas.
- Compromiso común:
- Pruebas unitarias para lógica pura y compleja o bases reutilizables.
- Pruebas de integración/E2E para flujos de negocio y límites HTTP/JSON o de UI.
Pruebas frágiles y dolor de refactorización
- Queja frecuente: refactorizar “sin cambiar el comportamiento” rompe muchas pruebas de bajo nivel.
- Diagnósticos: las pruebas son demasiado de caja blanca, abusan de mocks o afirman detalles internos irrelevantes.
- Estrategias mencionadas:
- Centrar las pruebas en el comportamiento observable externamente.
- Aceptar que algunas pruebas tempranas, enfocadas en la implementación, deberían eliminarse o evolucionar una vez existan pruebas de comportamiento de mayor nivel.
- Usar las pruebas más como un arnés de seguridad para refactorizar que como restricciones de diseño.
Dogmatismo, flujo de trabajo y métricas
- Debate sobre “nunca cambies código sin una prueba en rojo”: algunos lo ven como núcleo de TDD, otros como un ritual innecesario, especialmente durante el trabajo exploratorio.
- Escepticismo general sobre el TDD de imitación ciega, la cobertura obligatoria del 100% y la cobertura como KPI (ley de Goodhart).
- Varios enfatizan el contexto: el riesgo del dominio, el lenguaje (dinámico vs fuertemente tipado), los requisitos no funcionales y la habilidad del equipo deben guiar la estrategia de pruebas, no una única metodología universal.