BUSCANDO LA CALIDAD DE SOFTWARE EN EL CAMINO Y NO EN LA META (II)

En el anterior post, expliqué cómo tener un entorno adecuado para la aplicación de las prácticas de desarrollo guiada por pruebas unitarias (TDD) y de aceptación (ATDD).  En esta segunda parte, voy a explicar qué es TDD y cómo funciona.

Imaginemos que estamos por empezar a desarrollar una funcionalidad, ya sea que esté especificada como caso de uso, historia de usuario, o lo que usen, siempre nos hacemos la misma pregunta ¿Por dónde empezamos? Lo usual es que empecemos por el front-ent o el back-ent, dependiendo las responsabilidades en el equipo.  Y aún cuando escogemos el lado por el que vamos a programar, no existe una guía o un camino muy claro hacia el desarrollo final. Y casi siempre esperamos al final para construir los casos de prueba y validarlos. Pero ¿Qué tal si lo hacemos al revés? Es decir, creamos las pruebas o más bien por ejemplos, y luego escribimos código para asegurarnos de pasar cada prueba. Suena lógico ¿no?

Porque construir software no es como construir cualquier otra cosa, el software siempre va a ser puesto a prueba, ya sea por programadores, testers, analistas de calidad o usuarios. Siempre habrá un caso(obstáculo) que haya que superar. Por ello, TDD existe como una técnica para llegar al mejor código posible utilizando pruebas a superar.

Para usar esta técnica es muy importante tener en cuenta lo siguiente:

  • Gatear antes de caminar: esto es algo muy difícil mientras más experiencia se tiene. Tendemos a querer ver más allá de lo evidente, lo cual nos dictan a menudo en la universidad. Metemos más y más código para cubrir ciertas “contingencias”. Con TDD la consigna es muy simple, no nos adelantaremos a crear una lógica para lo que aún no se ha cuestionado. Pasos cortos pero seguros es mejor que pasos agigantados pero descontrolados.
  • No adelantar los test: Así como ponemos código donde no debe haber, solemos complicar los test con la intención de abarcar casos que solo están en nuestra cabeza. Cuántas veces les ha tocado revisar código que no saben por qué ha sido escrito. Pues bien, la respuesta es que esa prueba para la cual fue realizada quedó en la memoria (si es que aún la tiene) del programador. Gracias a TDD, primero documentamos esa prueba para la cual crearemos código. No habrá código sin prueba documentada.

Ahora, veamos el ciclo TDD:

Ciclo de TDD
Ciclo de TDD by Timebox

Rojo: consiste en ejecutar el código tal como está para comprobar si funciona para la nueva prueba especificada. Cuando no obtendremos rojos en nuevas pruebas, entonces nuestro código está listo.

Verde: aquí implementamos lo que llamaremos el mínimo código viable MVC (cambiando el término que corresponde a Minimum Viable Product por Minimu Viable Code) , que nos permitirá conseguir una prueba superada correctamente. No importa si el código está en “duro” o es poco óptimo. Aquí lo importante es lograr el objetivo.

Refactorizar: éste es el punto clave de la técnica, porque aquí nos preocupamos de la calidad del código, que si está refactorizado, que si usamos variables en lugar de código en “duro”, que si nos falta crear métodos, en fin. Ahora, no significa que en este punto y de un solo golpe armaremos todo el código sin probar. Nuevamente, tenemos que ir paso a paso. Es decir, refactorizar pensando en el MVC. Luego, en un siguiente ejemplo -más complejo, por supuesto- nuestro código necesitará mejorarse. No debemos adelantarnos en crear código cuando no lo necesitamos aún. La paciencia es una virtud, dicen por ahí, y vaya que he aprendido a serlo en todo lo que va de mi carrera. Así que no es un mal consejo que puedo dar. Seamos pacientes y no nos apresuremos a encontrar una solución perfecta. Lo perfecto es enemigo de lo bueno, de lo mínimamente indispensable.

Ahora, vamos a ejemplificarlo con un caso muy simple, utilizando la herramienta de pruebas de .NET. El objetivo en este ejemplo es crear un algoritmo haga la siguiente operación: saber en qué parte se encuentra ubicada una letra dentro de una palabra. Sí, un simple juego de ahorcado.

Comenzamos por una prueba/ejemplo. En este caso, supongamos que la palabra a adivinar es “Arbol”. Redactamos un primer ejemplo que nos dé un resultado:

“Se ingresa la letra L y como respuesta se obtiene _ _ _ _ L”

Creamos el método que realizará la simulación del intento. Asimismo, creamos un método evaluar que no retorne nada.

Ejecutamos la prueba y obtendremos un error. A partir del error trabajamos

Ya que este ejemplo pide un resultado, haremos que la función devuelva el valor “duro”

Bien. Ya que tenemos un Verde, ahora, el siguiente paso es preguntarnos si estamos orgullosos de nuestro código. Obviamente la respuesta es NO. Sin embargo, la respuesta es correcta y para este primer ejemplo, funciona. Por lo que pasaremos a hacer un segundo ejemplo. Para ello creamos un nuevo método que llamaremos “Intento 2”

“Se ingresa la letra A y como respuesta se obtiene A_ _ _ _”

Con este segundo ejemplo nos daremos cuenta que el primer código no nos servirá. Sin embargo, corremos la aplicación para validarlo. Como resultado, obtendremos un rojo.

Entonces, implementaremos una lógica que nos permita conseguir un verde para ambos ejemplos.

Sí, seguro pensarán que es un código terrible, pero de eso se trata, de ir cuestionándonos poco a poco la calidad de nuestro código. ¡Sigamos!

Luego de este verde, entonces, nos haremos nuevamente la pregunta ¿Orgullosos? En este caso, la respuesta vuelve a ser NO y realizaremos la refactorización y optimización del código.

Luego, incorporamos un tercer caso que consiste en una mezcla de los dos casos anteriores con la intención de hacer más real el juego, con intentos consecutivos.

Sin embargo, este último caso genera error debido a que el código que hemos implementado no considera intentos consecutivos:

Entonces, tenemos que crear un algoritmo más complejo para cumplir con lo requerido. Creamos un par de variables adicionales de forma global y el optimizamos el método evaluar.

Ejecutamos las pruebas y obtenemos que los tres casos resultaron exitosos:

De esta forma, hemos conseguido:

  • Implementación de código mínimo viable
  • Calidad en el código a través de pruebas superadas una a una.

Hasta el momento hemos superado pruebas unitarias. Sin embargo, cuando trabajamos con historias de usuario se redactan también criterios de aceptación. Estos criterios se convierten automáticamente en pruebas de aceptación e implementar código para pasar estas pruebas es el objetivo de ATDD (desarrollo guiado por pruebas de aceptación). Esto lo revisaremos con detenimiento en un siguiente post.

Please follow and like us:
0

Leave a comment

Hey, so you decided to leave a comment! That's great. Just fill in the required fields and hit submit. Note that your comment will need to be reviewed before its published.