Recursos programación

martes, 5 de junio de 2018

Capítulo 20: xUnit - TDD By Example

En este capítulo Kent Beck propone crear un «framework» de testeo.

La idea básica para este «framework» sería crear un caso de prueba y poder ejecutar un método de prueba de este caso. Esto se podría implementar teniendo una clase «TestCase» en la cual haya un método «run()» que se encargue de ejecutar el método de prueba.

A partir de aquí se plantean una serie de funcionalidades que debería tener el «framework»:

    • Ejecutar el método de prueba.
    • Ejecutar un «setUp» primero.
    • Ejecutar «tearDown» después.
    • Ejecutar «tearDown» incluso si el método falla.
    • Ejecutar multiples test.
    • Informar de los resultados.

La primera tarea a implementar es la de ejecutar el método de prueba. Para abordar esta tarea sería interesante saber si el método de prueba se ejecuta o no. Si tenemos un método de prueba que modifique un indicador («flag») dentro del método de prueba se puede saber con ese «flag» si el método de prueba se ha ejecutado.

Para implementar esta funcionalidad se va a partir de un ejemplo particular y se crea una clase “WasRun”, que es un caso de prueba, con un atributo “wasRun” que es el «flag» qué nos indicará si un método de prueba se ha ejecutado o no.

En este punto, la estrategia va a ser crear un guión («main.php») muy sencillo de cómo tendría que funcionar. El guión podría ser el siguiente:

En el script «main» se está creando una instancia de un caso de prueba («WasRun»), se imprime por pantalla el valor inicial de «wasRun» (el cual debería ser «false» y no imprimir nada), ejecuta el método de prueba «testMethod()» y vuelve a imprimir por pantalla el valor de «wasRun» que en este caso debería tener un valor de «true».

Si se ejecuta el script «main», este no funciona ya que la clase no está creada. El siguiente paso es crear la clase:

Si ejecutamos el script sigue fallando y necesitamos implementar el atributo «wasRun» e inicializarlo.

Si volvemos a ejecutar el script sigue fallando porque aunque se hayan implementado la clase y el atributo todavía quedaría por implementar el método.

Una vez definido el método, ejecutamos el script «main»  y este saca los valores que esperamos por la consola.

Este primer paso del desarrollo se ha basado en llamar al método de prueba de forma estática, es decir, haciendo la llamada al método de prueba «testMethod()» de forma explícita en «main».

Como se indicó en las especificaciones, las pruebas deberían usar la interface «run()» para ejecutar un método de prueba. Ahora es el momento de cambiar la llamada estática por una dinámica.

Para hacerlo dinámico debería inicializarse el método de prueba que se quiere ejecutar y ejecutar el método «run()». Para implementarlo modificamos el «main» de manera que se pase como parámetro en el constructor el nombre del método de prueba que se quiere ejecutar:

Siguiendo la metodología de TDD, se hace el desarrollo mínimo para que el «main» funcione como esperamos. Para conseguirlo, se implementa el constructor al cual añade un parámetro de entrada («methodName») que es el nombre del método de prueba a ejecutar, se implementa el método «run()» y, en su implementación se ejecuta el método «testMethod()» de forma estática (de momento sin utilizar ninguna variable). Este sería un pequeño paso para conseguir que «main» funcione.

Si ejecutamos «main», funciona como esperamos y la salida por consola sigue siendo la misma.

El siguiente paso es generalizar la llamada del método «run()» al método de prueba «testMethod()» usando una variable y no la llamada estática. Para ello se utiliza una llamada a un método variable. 

Al volver a ejecutar «main» se puede comprobar que todo sigue funcionando correctamente.

Esta forma de desarrollo en la que se parte de un ejemplo concreto para más tarde generalizar reemplazando constantes por variables, es un patrón general de refactorización.

Si observamos detenidamente, la clase «WasRun» tiene dos responsabilidades: ejecutar el método de prueba y almacenar si el método de prueba se ha ejecutado. Según el Single Responsibility Principle (SRP) sólo se debería tener una responsabilidad por lo que es el momento de seguir refactorizando.

Para separar las dos responsabilidades se extrae la ejecución del método de prueba a una clase padre «TestCase» que herede la clase «WasRun».


El método «run()» sólo utiliza atributos de la clase padre por lo que es lógico que esté en esa misma clase. Las operaciones deberían siempre estar cerca de los datos.


En cada uno de estos pequeños cambios se ha ido ejecutando el «main» comprobando que todo seguía funcionando correctamente.

El TDD brinda la posibilidad de trabajar en pequeños pasos aunque estos parezcan ridículos. Una vez se va cogiendo soltura con cambios pequeños estos pequeños pasos pueden albergar un conjunto de ellos.

Ya se puede tachar del listado la primera tarea:

    • Ejecutar el método de prueba.
    • Ejecutar «setUp» primero.
    • Ejecutar «tearDown» después.
    • Ejecutar «tearDown» incluso si el método falla.
    • Ejecutar multiples test.
    • Informar de los resultados.

Puedes ver un ejemplo funcionando en mi repositorio tdd by example.

No hay comentarios:

Publicar un comentario