← Volver al blog Post

TDD: El bueno, el malo y el testeado

Publicado originalmente en Medium.

En esta publicación me alejo un poco de la visión puramente técnica de otros artículos para compartir algo más personal: las sensaciones de mis primeros días en Lean Mind y parte del aprendizaje extraído de la formación interna que recibimos alrededor de TDD.

Antes de entrar en los ejercicios, conviene definir algunas ideas que estuvieron muy presentes durante esos días.

TDD

Durante años he escuchado todo tipo de opiniones sobre Test-Driven Development. Algunas personas lo defienden como una práctica fundamental. Otras lo consideran un sobrecoste innecesario. Mi conclusión es sencilla: conviene probarlo antes de juzgarlo.

TDD consiste en escribir antes el test que describe la funcionalidad deseada y dejar que el código aparezca como respuesta a ese caso de uso. El ciclo más conocido es Red, Green, Refactor:

  • Red: escribimos una prueba que falla.
  • Green: implementamos la solución más simple para que pase.
  • Refactor: limpiamos y mejoramos el código sin cambiar su comportamiento.

La idea puede sonar extraña al principio, sobre todo porque parece poco intuitivo escribir una prueba de algo que todavía no existe. Sin embargo, en cuanto se practica, empieza a verse que el test obliga a concretar mejor la necesidad y a no construir más de lo necesario.

Pair Programming

Otra práctica muy ligada a este aprendizaje es el Pair Programming: dos personas trabajando sobre el mismo código en un mismo terminal.

Normalmente distinguimos dos roles:

  • Driver: quien escribe el código y ejecuta la idea acordada.
  • Navigator: quien observa, detecta casuísticas, hace preguntas y ayuda a mantener el rumbo.

El valor real no está en corregir pequeños errores sintácticos sobre la marcha, sino en sostener una conversación de calidad sobre la solución. Los roles deben intercambiarse con frecuencia y adaptarse al contexto, a la dificultad de la tarea y al estado mental de la pareja.

Parece sencillo, pero sacarle partido requiere experiencia, escucha y bastante factor humano.

Arquitectura hexagonal

Otro concepto importante para aplicar TDD con comodidad es la arquitectura hexagonal, también conocida como patrón de puertos y adaptadores.

La idea de fondo es desacoplar el núcleo del sistema de servicios externos y de los detalles de infraestructura. En lugar de depender directamente de herramientas concretas, librerías o APIs, creamos envolturas o interfaces que nos permiten cambiar implementaciones y, sobre todo, testear el comportamiento sin arrastrar dependencias reales.

Cuando esto no existe, especialmente en legacy code, hacer TDD se vuelve mucho más difícil porque la arquitectura no está preparada para ser probada.

Katas

Las katas son ejercicios pequeños pensados para practicar. Su objetivo no es demostrar que somos capaces de llegar a la solución más rápido, sino entrenar una forma de pensar y de programar.

De hecho, cuando combinamos una kata con TDD es habitual que la solución cambie cada vez que repetimos el ejercicio. Lo valioso no es memorizar una implementación, sino mejorar la forma de llegar a ella.

Antes de empezar

Antes de lanzarnos a escribir código, es importante definir los casos de uso y un orden razonable para abordarlos. En TDD eso importa mucho porque queremos avanzar desde casos pequeños y concretos, sin asumir requisitos que nadie ha pedido.

También conviene recordar varias ideas prácticas:

  • No debemos enamorarnos del código. A veces toca borrarlo y volver a empezar.
  • Lo importante no es la cantidad de ingenio, sino la claridad de la solución.
  • Git es un gran aliado: hacer commits pequeños y expresivos ayuda a entender el progreso.

Una estrategia interesante es registrar el estado del trabajo cuando el test está en rojo y volver a hacerlo cuando pasa a verde. No es una regla universal, pero sí una buena forma de hacer visible el avance.

Kata: String Calculator

El primer ejercicio consistía en crear una función que recibiera una cadena con números separados por comas y devolviera su suma. Si aparecía una letra, debía contarse como 0.

La tentación habitual es intentar resolverlo todo de golpe. Con TDD, en cambio, la pregunta correcta es: ¿qué casos mínimos necesito demostrar primero?

Un posible planteamiento sería:

TODO:
param = '1' return 1
param = 'a' return 0
param = '' return 0
param = '1,1' return 2
param = '1,a' return 1
param = '1,1,1' return 3

La clave es resolver el caso más simple primero y aceptar que la primera implementación puede ser incluso un pequeño truco temporal. No estamos optimizando para el futuro, sino para el siguiente paso seguro.

Después se introducía un cambio de requisitos: permitir un separador personalizado con una sintaxis tipo //#;1#2#3. Ese cambio obliga a dejar que el código evolucione poco a poco, en lugar de sobrediseñar desde el principio.

Kata: Partir frases

En este ejercicio queríamos simular el comportamiento de un editor de texto al insertar saltos de línea según un número máximo de columnas. Si el corte caía en mitad de una palabra pero había un espacio anterior útil, el salto debía hacerse en ese espacio.

Un posible listado inicial de casos sería:

TODO:
'Hola', 5 return 'Hola'
'Hola', 2 return 'Ho\nla'
'Hola Mundo', 7 return 'Hola\nMundo'

Lo interesante aquí es que, a medida que avanzamos, empiezan a aparecer preguntas no resueltas. Por ejemplo: qué debe pasar si el número de columnas es 0.

La lección importante es no inventar nosotros la respuesta si no forma parte del requisito. Lo correcto es aclararlo con quien define la necesidad. En el ejemplo, decidimos que ese caso debía producir un error.

Este tipo de kata también enseña otra idea útil: renombrar variables y extraer intención suele aclarar el camino mucho más que seguir añadiendo lógica a ciegas. Con frecuencia, una solución recursiva termina siendo más legible que una acumulación de bucles y condicionales.

Kata: Password Validator

Otro ejercicio consistía en validar una contraseña con estas reglas:

  • Debe contener al menos una mayúscula.
  • Debe contener al menos un número.
  • Debe contener al menos una minúscula.
  • Debe tener una longitud mínima de 6 caracteres.

Aquí aparecía un matiz interesante. En ejercicios anteriores empezábamos por los casos más simples. En esta kata, en cambio, resulta más útil empezar por el caso válido completo y, a partir de ahí, ir comprobando qué ocurre cuando incumplimos cada regla.

TODO:
param = 'Abcde1' return true
param = 'Abcdef' return false
param = 'abcde1' return false
param = 'ABCDE1' return false
param = 'Abcde' return false

Eso recuerda que TDD no es una receta mecánica. El orden correcto depende del problema y de cuál sea la forma más clara de descubrir la lógica.

Conclusión

Aplicar TDD no consiste solo en escribir tests antes que código. Va mucho más allá: obliga a concretar necesidades, a cuestionar supuestos y a dejar que la solución evolucione de la forma más simple posible.

Con el tiempo he dejado de perseguir tanto la idea de una solución supuestamente óptima y he pasado a valorar mucho más otra cosa: una solución sencilla, ajustada a la necesidad y fácil de entender para otras personas.

Por eso me sigue pareciendo valioso este enfoque:

  • Los tests específicos ayudan a que el código termine siendo más genérico y mantenible.
  • El código demasiado ambicioso desde el primer minuto suele salir peor.
  • Practicar con katas, pair programming y refactor continuo mejora tanto la técnica como la forma de pensar.

TDD no tiene por qué ser una obligación universal, pero sí una herramienta que merece una oportunidad real. Practicarla durante un tiempo cambia la forma de afrontar los problemas y ayuda a construir software con más criterio.

  • tdd
  • testing
  • pair-programming
  • software-design