Avoid Testing Implementation Details, Test Behaviours / Evita testear detalles de implementación, testea comportamiento

Traducción del texto original de Ian Cooper el 6 de Octubre de 2011 en http://codebetter.com/iancooper/2011/10/06/avoid-testing-implementation-details-test-behaviours/

De vez en cuando vuelvo a leer el libro Test-Driven Development de Kent Beck. Sinceramente, creo que es uno de los mejores libros de desarrollo de software jamás escritos. Lo que me encanta del libro es su sencillez. Tiene una austeridad que engaña, como si se tratara de una aproximación superficial del tema. Pero si continúas leyendo a medida que avanzas con TDD te das cuenta que es bastante completo. Donde trabajos posteriores a este libro han agregado páginas e ideas, a menudo han disminuido la técnica, cargándola de complejidad, generando malentendidos acerca del poder de TDD. Insto a cualquiera de vosotros que lo haya practicado TDD por un tiempo, a volver a leer este libro regularmente.

Para mí, una de las ideas clave de TDD, que a menudo se pasa por alto, se expresa en el ciclo: Red, Green, Refactor. Primero escribimos una prueba que falle, luego la implementamos el código de la manera más rápida y sucia que podemos, y luego la hacemos elegante. En este post del blog, quiero hablar sobre algunas de las ideas del segundo y tercer paso que se pierden. (Aunque debe recordar el primer paso: las pruebas no tienen pruebas, por lo que debe probarlas).

Mucha gente se bloquea cuando van a implementar, porque tratan de implementar bien, pensando en buenos patrones OO, SOLID, etc. ¡Alto! El objetivo es poner el test en verde lo antes posible. No intentes crear una solución decente en este punto, simplemente escribe la solución más rápida para el problema que puedas encontrar. Corta y pega si puedes, copia algoritmos de CodeProject o StackOverflow si puedes.

Del libro Test-Driven Development.

Detente. Espera. Puedo escuchar a los «estirados» burlándose entre ustedes y votimando. ¿ copiar y pegar? ¡ El fin de la abstracción ! ¡ La muerte del clean code !

Si estás molesto, respire profundamente. Aspira por la nariz … aguanta 1, 2, 3 … expira por la boca. Ahora. Recuerda, nuestro ciclo tiene diferentes fases (pasan rápidamente, a menudo en segundos, pero son fases):

1. Escriba una prueba.

2. Haz que compile.

3. Ejecútalo para ver si falla.

4. Hazlo funcionar.

5. Elimina la duplicación .

Es ese paso final (que es nuestro paso de refactorización) es en el que hacemos clean code. No antes, sino después.

Hacer clean code al mismo tiempo que hacemos el paso «que funcione» puede ser demasiado para hacer a la vez. Tan pronto como puedas, comprueba el paso «hágalo funcionar» y luego «limpie el código» cuando lo desees.

Ahora bien, aquí hay una implicación importante que a menudo se pasa por alto. No tenemos que escribir tests para las clases que refactorizamos a través de ese último paso. Serán implícitos a nuestra implementación y ya están cubiertos por la primera prueba. No es necesario que agreguemos nuevas pruebas para el siguiente nivel, a menos que consideremos que no sabemos cómo dirigirnos al siguiente paso.

Del libro Test-Driven Development nuevamente:

Debido a que estamos usando Pairs como claves, tenemos que implementar equals() y hashCode(). No voy a escribir tests para estos métodos, porque estamos escribiendo código en el contexto de una refactorización. Si estamos en el paso de refactorización y pasan todos los tests, entonces suponemos que ese nuevo código se ha ejercitado correctamente. Si estuviera programando con alguien que no viera exactamente a dónde vamos con esto, o si la lógica se volviera un poco compleja, comenzaría a escribir pruebas por separado.

¡ El código desarrollado en el contexto de la refactorización no requiere nuevos tests ! Ya está cubierto, y las técnicas de refactorización seguras significan que no deberíamos estar introduciendo cambios específicos, sólo limpiando la implementación aproximada que solíamos hacer en verde. En este punto, más pruebas te ayudarán a dirigir el test (pero tienen un coste)

El aprendizaje es que un test por clase no es la esencia de TDD. Agregar una nueva clase no es el desencadenante para escribir un test. El motivo que lleva a escribir un test es implementar un requisito. Por lo tanto, deberíamos probar de afuera hacia adentro (aunque recomendaría usar puertos y adaptadores y hacer el ‘afuera’ del puerto), escribir pruebas para cubrir los casos de uso (escenarios, ejemplos, GWT, etc.), pero sólo escribir pruebas para cubrir los detalles de implementación de eso cuando necesitemos comprender mejor la refactorización de la implementación simple con la que comenzamos.

Un resultado positivo de esto será que gran parte de nuestra implementación será interna o privada, y nunca se expondrá fuera. Eso reducirá el acoplamiento de nuestra solución y facilitará que efectuemos el cambio.

Del libro Test-Driven Development nuevamente:

En TDD usamos la refactorización de una manera interesante. Por lo general, una refactorización no puede cambiar la semántica del programa bajo ninguna circunstancia. En TDD, las circunstancias que nos preocupan son los tests que ya están pasando.

Cuando refactorizamos, no queremos romper los tests. Si nuestros tests saben demasiado sobre nuestra implementación, será difícil refactorizar, porque los cambios en nuestra implementación necesariamente darán como resultado que reescribamos los tests, momento en el que no estamos refactorizando. Diríamos que hemos sobreespecificado a través de nuestros tests. En lugar de ayudar al cambio, nuestros tests ahora han comenzado a ser un obstaculo. Como alguien que ha cometido este error, puedo dar fé del hecho de que es posible escribir demasiados tests unitarias para testear detalles. Sin embargo, si seguimos TDD como se pensó originalmente, los tests caerán de forma natural en mayor medida en nuestra interfaz pública, no en los detalles de implementación, y nos resultará mucho más fácil cumplir con el objetivo de cambiar los detalles de impementación (cambio seguro) y no la interfaz pública (cambio inseguro). Por supuesto, esta observación, que muchos practicantes de TDD han pasado por alto esta idea, formó el base de BDD (original de Dan North) y es la razón por la que BDD se centró en escenarios y se enfoca afuera hacia adentro (inside-out) para TDD.

Deja una respuesta

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

 

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.