2.2.1. Ventajas del desarrollador experto frente al junior
Existe la leyenda de que TDD únicamente es válido para personal altamente cualificado y con muchísima experiencia. Dista de la realidad; TDD es bueno para todos los individuos y en todos los proyectos. Eso sí, hay algunos matices.
La diferencia entre el desarrollador experimentado que se sienta a hacer TDD y el junior, es cómo enfocan los tests, es decir, qué tests escriben; más allá del código que escriben.
El experto en diseño orientado a objetos buscará un test que fuerce al SUT a tener una estructura o una API que sabe que le dará buenos resultados en términos de legibilidad y reusabilidad. Un experto es capaz de anticipar futuros casos de uso y futuros problemas y será más cuidadoso diseñando la API test tras test, aplicando las buenas prácticas que conoce.
El junior probablemente se siente a escribir lo que mejor le parece, sin saber que la solución que elige quizás le traiga quebraderos de cabeza más adelante. La ventaja es que, cuando se dé cuenta de que su diseño tiene puntos a mejorar y empiece a refactorizar, contará con un importantísimo respaldo detrás en forma de batería de tests. Por poco experimentado que sea, se cuidará de no diseñar una API que le resulte casi imposible de usar.
Debe tenerse en cuenta que se supone que el principiante no está solo, sino que en un contexto XP, hay desarrolladores de más experiencia que supervisarán y habrá momentos en los que se programe en parejas. La figura de los líderes es importante en XP al igual que en otras metodologías, con la gran diferencia de que el líder ágil está para responder preguntas y ayudar a los demás y no para darles látigo. El líder debe intentar que las personas que trabajan con él estén contentas de trabajar ahí y quieran seguir haciéndolo.
2.2.2. TDD con una tecnología desconocida
La primera vez que usamos una determinada tecnología o incluso una nueva librería, es complicado que podamos escribir la especificación antes que el SUT, porque no sabemos las limitaciones y fortalezas que ofrece la nueva herramienta.
En estos casos, XP habla de spikes (disculpen que no lo traduzca, no sabría como). Un spike es un pequeño programa que se escribe para indagar en la herramienta, explorando su funcionalidad. Es hacerse alguna función o alguna aplicación pequeña que nos aporte el conocimiento que no tenemos.
Si el spike es pequeño, y resulta que nos damos cuenta que su propio código es válido tal cual, entonces escribiremos el test justo a continuación, en lugar de dejarlo sin test. Sin un conocimiento básico de la API y las restricciones del sistema, no recomendaría lanzarse a escribir especificaciones. Hay que respetar el tiempo de aprendizaje con la herramienta y avanzar una vez que tengamos confianza con ella. Intentar practicar TDD en un entorno desconocido es, a mi parecer, un antipatrón poco documentado.
Tampoco es que descartemos forzosamente TDD, sino que primero tendremos que aprender a pilotar la máquina. Una vez sepamos si es de cambio manual o automático, dónde se encienden las luces y dónde se activa el limpia parabrisas, podremos echar a rodar. Es sólo cuestión de aplicar el sentido común, primero aprendemos a usar la herramienta y luego la usamos. Tenemos que evitar algo que pasa muy frecuentemente, minusvalorar el riesgo de no dominar las herramientas (y frameworks y lenguajes...)
2.2.3. TDD en medio de un proyecto
En la segunda parte del libro, la de los ejemplos prácticos, iniciamos el desarrollo de una aplicación desde cero. Igual que hacemos en los cursos que imparto. La pregunta de los asistentes aparece antes o después: ¿no se puede aplicar TDD en un proyecto que ya está parcialmente implementado? Claro que se puede, aunque con más consideraciones en juego.
Para los nuevos requisitos de la aplicación, es decir, aquello que todavía falta por implementar, podremos aplicar eso de escribir el test primero y luego el código (¡y después refactorizar!). Es probable que el nuevo SUT colabore con partes legadas que no permiten la inyección de dependencias y que no cumplen una única responsabilidad; código legado que nos dificulta su reutilización.
El libro más recomendado por todos en los últimos tiempos sobre este asunto es, Working Effectively with Legacy Code de Michael C. Feathers. Tratar con código legado no es moco de pavo. En general, por código legado entendemos que se trata de aquel que no tiene tests de ningún tipo.
Mi recomendación, antes de ponerse a reescribir partes de código legado, es crear tests de sistema (y cuando el código lo permita, tests unitarios) que minimicen los posibles efectos colaterales de la reescritura. Si es una web, por ejemplo, agarrar Selenium o similar y grabar todos los posibles usos de la interfaz gráfica para poderlos reproducir después de las modificaciones y comprobar que todo el sistema se sigue comportando de la misma manera.
Es un esfuerzo de usar y tirar porque estos tests son tremendamente frágiles, pero es mucho más seguro que lanzarse a reescribir alegremente. La siguiente recomendación es que la nueva API y la vieja convivan durante un tiempo, en lugar de reescribir eliminando la versión legada. Además de tener dos API podemos sobrecargar métodos para intentar que el código legado y su nueva versión convivan, si es que la API antigua nos sigue sirviendo. Viene siendo cuestión de aplicar el sentido común y recordar la ley de Murphy; "Si puede salir mal, saldrá mal".
Otra alternativa para hacer TDD con código nuevo que colabora con código legado es abusar de los objetos mock. Digamos que los tests van a ser más frágiles de lo que deberían pero es mejor usar paracaídas que saltar sin nada. Y por supuesto, si el nuevo código es más independiente, podemos seguir haciendo TDD sin ningún problema. Se lo recomiendo encarecidamente.