BDD (Behaviour Driven Development) en Java con Cucumber

Una de las ideas que hay detrás del TDD, es que los propios test sirven para documentar el código y definir su funcionamiento (perdonad si me río un poco…). Con BDD, nos acercamos un poco a este fin.

BDD, Behaviour Driven Development, o lo que es lo mismo, desarrollo guiado por comportamiento, fue desarrollado por Dan North y es una vuelta de tuerca más al concepto de TDD y ATDD (desarrollo dirigido por test de aceptación). No pretendo soltar un rollo teórico aquí sobre BDD, para eso ya está la Wikipedia, pero si voy a dar un par de pinceladas.

¿Qué ofrece BDD?

 Uno de los problemas que nos encontramos al desarrollar software es que el cliente/analista de negocio habla un lenguaje y los desarrolladores otro, más pegado a la tecnología y las soluciones que se van a aplicar. Sus principales características son:

  • Sirve de puente de comunicación entre el cliente/analista del negocio y el desarrollador
  • Permite utilizar un lenguaje común entre los desarrolladores y los clientes
  • Tiene un enfoque de fuera hacia adentro, esto es, se parte de los criterios de aceptación hacia la implementación
  • Se utiliza un lenguaje ubicuo

Gherkin

Gherkin, o lo que es lo mismo, pepinillo, es el nombre que le han dado al lenguaje de dominio utilizado para definir los criterios de aceptación de cada una de las funcionalidades a desarrollar. Consta de estos tres elementos:

  • GIVEN: Dado un determinado contexto o situación inicial
  • WHEN: Cuando se produce un determinado evento
  • THEN: Entonces, se produce un determinado resultado

Lo interesante de este enfoque es que nos permite estandarizar la definición de los requisitos y utilizar herramientas que automaticen el proceso. En este artículo vamos a utilizar Cucumber, que es una herramienta muy potente.

En este artículo vimos un ejemplo sencillo de TDD, hoy vamos a dar un pasito más allá y veremos varios ejemplos de BDD.

Dependencias Maven para el uso de Cucumber

A continuación se muestran las dependencias necesarias tanto para el uso de JUnit como de TestNG

Definir las características de la funcionalidad (features)

Nuestro primer ejemplo será una calculadora.

Como hemos comentado anteriormente, el mecanismo para definir la funcionalidad a desarrollar se realiza a través del lenguaje de dominio “Gherking”. Esta definición se realiza en un archivo .feature, que colocaremos en el paquete “src/test/resources/com/notodocodigo/feature/calculadora.feature”. El contenido del fichero será el siguiente:

Como se puede ver, el archivo tiene un elemento “Feauture”, donde se define la funcionalidad a desarrollar. También podrá contener varios elementos “Scenario”, con cada una de los escenarios o situaciones que se van a desarrollar. Cada escenario constan de los elementos Gherkin que ya hemos descrito.

Ejecutar Cucumber con JUnit

Bien, ya tenemos descrita nuestra funcionalidad, ahora nos toca ejecutar esta feature en un test JUnit o TestNG. En este ejemplo utilizaremos JUnit. Vamos a hacer “magia”, para ello creamos la siguiente clase en el paquete “/src/test/java/com/notodocodigo/RunCukesTest.java”:

Esta clase está en el paquete “com.notodocodigo”, por lo tanto lanzará la ejecución de todos los archivos .features que se encuentren en este paquete o en sus hijos. Si ejecutamos la clase de test, se nos muestra por consola lo siguiente:

A que moooooooooola! Cucumber nos está diciendo que hemos definido un escenario con sus tres steps (given, when, then), pero que no los hemos implementado. Además nos proporciona el código necesario para implementar la clase de prueba.

Implementando nuestra primera clase de test

Vamos a crear en el paquete “src/test/java/com/notodocodigo/steps” nuestra primera clase de test “CalculadoraStep.java” y vamos a copiar el código  que se nos ha mostrado en la consola en el punto anterior:

Si ejecutamos de nuevo el test, obtendremos el siguiente resultado:

Ya disponemos de los métodos, pero no los hemos implementado. Como se puede ver, los métodos reciben los argumentos que definimos en nuestro archivo .feature, es decir, el método “sumo_los_valores_y” recibirá los valores 23 y 14, y el método “el_resultado_es” recibirá el valor 37. A mi personalmente no me gusta los nombres que Cucumber le da a estos métodos, así que los voy a cambiar para que sigan las convenciones de código de Java.

Siguiendo ahora la mecánica de TDD, implementaremos el código del test:

 Y seguidamente, en el paquete “/src/main/java/com/notodocodigo/calc” la implementación de nuestra calculadora “Calculadora.java”:

Si ejecutamos de nuevo el test “RunCukesTest.java”, el resultado es el siguiente:

Resultado test Cucumber

Resultado test Cucumber

 

Una cosa muy interesante, es que podemos editar nuestro archivo “calculadora.feature” y añadir más situaciones de prueba, como que 3 + 6 = 9, etc. y tendremos nuevos casos de prueba sin escribir una sola línea de código.

Cucumber utiliza expresiones regulares para encontrar el método que se corresponde con cada step definido en cada escenario, a continuación veremos como

Expresiones regulares y paso de parámetros en Cucumber

Cucumber machea el contenido de los archivos .feature con los métodos a invocar a través de las anotaciones que ya hemos visto y de expresiones regulares. Como por ejemplo:

When Sumo los valores 23 y 14

Se corresponde con

@When(“^Sumo los valores (\\d+) y (\\d+)$”)

La expresión regular comienza con el símbolo “^” y termina con “$“. Además, Cucumber será capaz de recuperar los argumentos y convertirlos al tipo que necesite el argumento de entrada del método. Veamos algunos ejemplos de expresiones que podemos utilizar:

Expresiones regulares Cucumber

Expresiones regulares Cucumber

Scenario outlines

El uso de scenario outlines, permite el uso de expresiones más concisas mediante el uso del marcador “<>”. Veamos un ejemplo:

Se ejecutará una prueba, por cada entrada en la tabla. Implementamos en nuestra calculadora un método que nos calcule el cuadrado. Entonces podremos ejecutar lo siguiente:

 Paso de objetos

Imaginemos que tenemos una clase usuario, como la siguiente:

Y queremos implementar una clase que nos de su descripción en base al siguiente feature:

Podemos escribir la siguiente clase de prueba:

Implementamos la funcionalidad, de este modo:

Al ejecutar el test comprobamos que los resultado esperados son los correctos. Podemos probar a cambiar una letra del resultado esperado en el archivo .feature y comprobar los resultados.

También podemos mapear tablas de datos de nuestro .feature a tipos TableData y luego extraer la información de este objeto en la clase de test, de este modo:

Conversión de datos

Vamos a ver algunos ejemplos de conversión implícita de datos. Dado el siguiente feature:

Podemos recibir los datos del siguiente modo:

También podemos implementar Transformer.java, para mapear cualquier tipo de objeto, por ejemplo:

E implementaríamos la clase de este modo:

Aquí terminamos el artículo. Espero que os haya parecido interesante.

Recordad que los comentarios son moderados y pueden tardar en mostrarse.

 

 

 

Deja un comentario

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