TDD: El contexto es el rey

¿Así que quieres empezar a hacer TDD eh? has leído todos los blogs sobre el tema, visto todos los videos en la web y ahora estás listo para comenzar. Abres tu editor de código de elección y comienzas un nuevo proyecto y un nuevo proyecto de prueba. Y te quedas mirando. Sabes lo que tienes que hacer: escribir una prueba, hacerla fracasar, escribir el código más simple para hacerla pasar, refactorizar el código para que se vea bien. Y volvemos a empezar. El punto es, que realmente no sabes qué probar. O cómo saber qué probar. ¿Todos hemos estado ahi con la cara en blanco y un solo pensamiento: por dónde empiezo?

El principal problema es la falta de experiencia. Realmente no puedo pensar en otra cosa. Supongo que no es un problema cuando se está haciendo TDD con alguien que ya tiene experiencia en esto, pero si este no es tu caso no te preocupes, sólo tienes que hacer una cosa: empezar.

Supongo que es diferente para todos pero quiero compartir algunas de mis propias prácticas personales sobre este asunto. Espero que esto te ayuda a empezar.

  1. Definir las acciones del sistema.
  2. Definir los objetos de negocio en base a cada caso de uso.
  3. Escribir la primera prueba para un objeto de negocio.
  4. Crear interfaces para cualquier dependencia no definida todavía.

Definir las acciones del sistema

Uno de los errores más comunes que he visto son la falta de alcance y contexto. Sé lo que voy a decir suena tonto pero realmente, realmente, necesitas sentarte a averiguar lo que el sistema va a hacer antes de escribir algo de código. He visto tantas personas fracasando en esto!! Mi técnica favorita para esto es usar diagramas de casos de uso UML. Para empezar realmente no me enfoco demasiado en los detalles, solo quiero tener una idea general del alcance del sistema y los usuarios involucrados. Un diagrama de casos de uso permite ver cómo los actores van a interactuar con el sistema y las acciones específicas que un rol/personaje puede hacer con él. Cuando se trabaja en un entorno ágil he encontrado que éstos casos de uso corresponden muy bien con historias de usuario e incluso con epics si utilizas scrum. Recuerda que esta es una vista de alto nivel del sistema. No te enredes demasiado con los detalles. Aún mejor, no pienses en los detalles para nada. Todavía no.

Definir los objetos de negocio en a por base del caso de uso

Una vez que has definido los casos de usos para el sistema, selecciona uno. Ahora puedes entrar en los detalles (mas o menos). Aquí yo suelo utilizar un diagrama de interacción UML para definir la forma en que los objetos cooperarán para lograr los objetivos del caso de uso. Resiste la tentación de comenzar a dibujar los detalles de implementación: ahora lo único que te importa son las cosas que solicitas hacer al objeto y lo que esperas recibir de él. En otras palabras las entradas y salidas de lo objetos. Recuerda que estamos hablando de objetos de negocio, a menos que es necesaria para el flujo principal, no pongas ningún objeto relacionado con la infraestructura, es decir objetos que encapsulan llamadas a base de datos, servicios web y otras cosas que no tienen lógica del negocio y que solo están destinados a ser utilizados por otros objetos.

Escribir la primera prueba para un objeto de negocio

Al fin podemos codificar! Primero seleccione el caso de uso en el que deseas enfocarte. Ya? Ahora crea un archivo para contener las pruebas relacionadas con uno de los objetos de negocios. He adoptado algunas practicas del movimiento BDD y generalmente agrego la palabra «specs» (de specifications) al nombre del archivo. Así que vamos a decir que tengo un objeto de negocio llamado Cuenta, el archivo de prueba sería nombrado CuentaSpecs. Y digamos que este objeto particular tiene un método llamado Retirar, entonces quizás crearia algunas pruebas llamadas DebeRestarLosFondosAlRetirar, DebeFallarElRetiroSiNoTieneFondosSuficientes y así sucesivamente. Nota que en todas mis pruebas comencé con la palabra «debería». Es otra practica adoptada de BDD. Me gusta porque es más fácil expresar cual es el comportamiento esperado. Al final hay que recordar que no estas escribiendo pruebas, estas diseñando código. Por lo menos la implementacion de los métodos en los objetos. Por cierto, no te preocupes si decides hacer las cosas de una manera diferente al diagrama de interacción, esto es parte del show también y sucede muy a menudo que una buena idea resulta para ser demasiado compleja en el momento de codificarla. No tengas miedo a probar varios enfoques.

Crear interfaces para cualquier dependencia no definida todavía

Si al mismo tiempo de codificar descubres una dependencia a un objeto aún no creado, define una interfaz. No te quiebres la cabeza tratando de crear un objeto para proporcionar el servicio que necesitas, esto probablemente sea implementado por otro objeto mas adelante. Ademas de que esta fuera del alcance de la prueba. Así que define la interfaz con los métodos que necesitas y usala. No te sientes mal si tienes interfaces con solo 1 o 2 métodos, está bien. También es un efecto secundario a TDD y uno bueno:) (busca en google el principio de segregación de interfaz). De todos modos ahora necesitas un objeto que implemente la interfaz para usarlo en tu prueba. Puedes escribir uno. O mejor aun puedes usar mocks. Te recomiendo irte por esta ultima opción. No deberías perder tiempo escribiendo objetos que están fuera del alcance de lo que estas probando. Realmente no agrega valor. Busca en la red alguno de los muchos frameworks de mocking disponibles para la plataforma que estas usando. Experimenta hasta que encuentres uno con el que te sientas cómodo.

Algunas últimas palabras (y advertencias)

El propósito de todo lo que he cubierto aquí es para ayudarte a empezar. Conforme vayas obteniendo experiencias puedes omitir algunos de los pasos descritos aquí. Tal vez empieces a crear pruebas que representan el caso de uso conjunto en lugar de utilizar un diagrama de secuencia (yo lo he hecho). Pero si estas iniciando, te recomiendo seguir el procedimiento que he descrito arriba. Esto te dará algo de valor para empezar practicar TDD: contexto. ¿Qué probar? ¿qué no probar? ¿dónde empezar? que todo depende del contexto. Una vez establecido, las respuestas a estas preguntas fluirán naturalmente. El resto del procedimiento ya lo aprendiste de los libros, podcasts y post como éste: verde, rojo, refactorizar. También tengo una advertencia: no trates de escribir todas las pruebas desde el principio. No pienses en repasar todos casos de uso tratando de identificar cada prueba de antemano. Esta es una mala idea. La razón es simple: están diseñando código no escribiendo pruebas. Las pruebas son sólo una herramienta de diseño, como pluma y papel. Y al diseñar el código, hay varias decisiones que se tienen que hacer cuando algo inesperado surge en el código. También algunas pruebas darán lugar a la creación de otras.

Así que ahí lo tienen. Como siempre los comentarios son bienvenidos. Si tienes algún tip, sugerencia o una alternativa, me encantaría leerlo!