Cómo NO escribir tests unitarios: Todos a la vez

En los anteriores posts sobre errores que he cometido demasiadas veces escribiendo tests unitarios he estado repasando algunos detalles de implementación que pueden suponer problemas cuando escribimos tests unitarios, como usar en el test la lógica testeada, depender de otros constructores o depender de APIs no relevantes para el test.

Este post no va sobre código, sino sobre metodología, así que igual resulta menos entretenido para los amantes de la pornografía. Además lo que voy a explicar sólo tiene sentido si usas TDD, por lo que si estás escribiendo los tests después del código de producción, de poco te va a servir.

La teoría

Seguro que no os descubro nada nuevo si digo que TDD se basa en la repetición del bucle Red-Green-Refactor, o lo que es lo mismo:

  • Escribir un test que falla (Red, porque se pone la bolita del TestRunner en rojo).
  • Escribir el código imprescindible que hace pasar el test (Green, porque se pone la bolita en verde).
  • Refactorizar el código que tenemos.
  • Volver a empezar.

En teoría no debemos salirnos de ese bucle, no debemos escribir código de producción hasta que tengamos un test que lo demande y no debemos escribir más código del necesario para pasar el test.

La realidad

Seguro que tampoco os descubro nada nuevo si digo que en la práctica muchas veces estos pasos se relajan y las cosas son un poco más… anárquicas.

Un problema que he visto bastantes veces es que el Red-Green-Refactor se convierte en Red-Red-Red-Red-Green-Refactor, es decir, escribimos muchos tests tratando de cubrir todos los casos antes de empezar a escribir el código de producción.

Esto suele pasar porque nos adelantamos a los acontecimientos, creemos que tenemos claras las especificaciones de lo que estamos testeando y los casos límite que queremos probar. Para evitar que se nos olvide ningún caso, o para aclararnos nosotros mismos, empezamos a escribir tests para formar una especificación de requisitos.

A priori esto puede parecer algo bueno, por algo los tests son una especificación de requisitos ejecutable que nos guía en la implementación.

Sin embargo, escribir los tests demasiado pronto se puede acabar convirtiendo en un problema por varios motivos.

Por una parte, es muy fácil escribir tests de más o de menos. Estamos “adivinando” cuales van a ser los casos límites antes de tener claro el algoritmo que vamos a usar, por lo que puede que hagamos mucho hincapié en cosas innecesarias y dejar de lado otras que deberíamos testear más en profundidad.

Además, no es tan raro que al comenzar a implementar el código de producción te des cuenta de que es necesario cambiar el API, por ejemplo, porque necesita recibir otro parámetro. Si cambia el API testeada, hará falta modificar todos los tests que hemos escrito demasiado pronto.

Finalmente, perdemos una de las ventajas fundamentales de aplicar TDD. Perdemos la posibilidad de atacar un problema progresivamente, realizando un diseño incremental conforme vamos descubriendo más cosas del problema que estamos tratando de resolver. En lugar de eso, intentamos resolver el problema de una sola vez para que pase todos los tests y eso puede resultar más complicado que resolverlo poco a poco.

Alternativas

La mejor forma de evitar este problema es aplicar TDD al pie de la letra, pero es cierto que hay veces que resulta útil escribir una lista detallada de especificaciones antes de empezar a implementar el código.

El problema es convertir esa lista en tests completos, no el hecho de tener la lista, por lo que si te resulta útil tener la lista de especificaciones, puedes escribirla como tests vacíos, con un Assert.Fail o similar, que sirvan de recordatorio de casos que quieres testear más adelante.

Esto no evita que escribas tests de más, pero siempre será mejor eliminar un test que te ha costado 10 segundos escribir que uno que te ha llevado más tiempo. Aun así, no pierdas de vista lo que estás implementando y plantéate cada poco tiempo si los tests que escribiste antes son los correctos, para no acabar con tests de más o de menos.

Conclusiones

Si vas a aplicar TDD, aplícalo con coherencia y no escribas todos los tests antes, porque no te va ayudar mucho y sí puede hacerte trabajar más inútilmente.

Hay situaciones en que puede tener sentido escribir todos los tests antes, como cuando no es fácil realizar una implementación incremental, por ejemplo con ciertas consultas SQL, pero no son muy frecuentes y hay que tratarlas con cuidado para evitar los problemas que he explicado antes.


Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *

*

Puedes usar las siguientes etiquetas y atributos HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>