Sobre el uso de Gherkin/Cucumber y esa costumbre de dormir en la mesa

Toda herramienta es creada con un fin y luego usada como a cada uno le parece. El ejemplo que siempre pongo es el de la mesa: podríamos pensar que la mesa fue pensada para sentarse a comer o a trabajar, pero sin embargo alguien podría usarla para acostarse a dormir. Esto puede traer dos riesgos: 

  1. que no resulte muy cómodo/conveniente o incluso que tenga limitaciones, «¡que incómodo dormir en la mesa! => es razonable ya que no fue pensada para eso»
  2. que posiblemente haya una herramienta más apropiada => ¿qué tal si probas dormir en la cama?

(tengo la sensación de que esta situación de «dormir» en la mesa se repite frecuentemente en la industria del software con distintas herramientas)

En este sentido Gherkin fue pensado como una herramienta de colaboración/comunicación/especificación para ser utilizada de programar la solución y que nos trae el beneficio adicional de poder automatizar utilizando alguna herramienta Gherkin-compatible como Cucumber, Cucumber-jvm, Behat, etc, etc.

Sin embargo vemos en la industria un uso alternativo de Gherkin/Cucumber: la automatización de pruebas una vez que la solución ya está programada. Si bien este escenario esalgo perfectamente válido a mi no me resulta conveniente. O sea: si no vamos a tener el involucramiento del usuario y no vamos a trabajar colaborativamente en entender el negocio antes de empezar a programar y solo buscamos automatizar pruebas, me resulta entonces más práctico programar las pruebas con alguna herramienta de la familia xUnit o Rspec. Un escenario en el que automatizar con Gherkin/Cucumber «a posteriori de la programación» es cuando tenemos una persona describiendo la prueba (erscribiendo Gherkin) y otra persona escribiendo el código de automatización por detrás del Gherkin. En lo personal sigue sin convencerme.

Pero al margen de mis gustos y convicciones, hay una diferencia importante en el uso de Gherkin dependiendo de la intención con la que lo estamos usando. Más concretamente la forma en la que vamos a escribir los escenarios con Gherkin va a ser distinta. Veamos un ejemplo, tomemos una funcionalidad de login, si estamos usando Gherkin como herramienta de colaboración/comunicación/especificación y seguimos las recomendaciones de los creadores, podríamos tener lo siguiente:

Antecedentes:
  Dado que existe un usuario "juan" con clave "claveFuerte123"

Escenario: Login exitoso
  Cuando un usuario ingresa credenciales "juan" y "claveFuerte123"
  Entonces el login exitoso

Escenario: Login fallido por clave incorrecta
  Cuando un usuario ingresa credenciales "juan" y "claveIncorrecta"
  Entonces el login es fallido

Algunos puntos para destacar:

  • El foco está en el comportamiento omitiendo todo detalle de implementación
  • No hay ninguna mención a elemento de presentación/pantallas/html
  • Los escenarios son muy cortos

Veamos ahora una versión alternativa que típicamente podría ser creada «post-programación» con el fin de automatizar una prueba de regresión.

Antecedentes:
  Dado que existe un usuario "juan" con clave "claveFuerte123"

Escenario: Login exitoso
  Dado que en el campo "usuario" pongo "juan"
  Y que en el campo "clave" pongo "claveFuerte123"
  Cuando hago click en el "Login"
  Entonces veo el mensaje "Bienvenido"

Escenario: Login fallido por clave incorrecta
  Dado que en el campo "usuario" pongo "juan"
  Y que en el campo "clave" pongo "claveIncorrecta"
  Cuando hago click en el "Login"
  Entonces veo el mensaje "Credenciales incorrectas"

A diferencia del otro caso aquí vemos mención a los elementos de presentación/pantalla y un detalle «del cómo», esto se debe a que el escribir el Gherkin de esta segunda forma permite reutilizar código de automatización. O sea, el paso «Dado que en el campo <x> pongo <v>» es completamente genérico y lo puedo utilizar tanto en el escenario de login como en cualquier otro. Por otro lado el paso «Cuando un usuario se logueo con credenciales «juan» y «unaClaveIncorrecta» es muy específico del login.

Entonces:

  • En un contexto donde el objetivo central es la automatización, será mucho más probable encontrarnos con Gherkin del segundo estilo.
  • En un contexto donde el objetivo es entender/comunicar será más probable (y conveniente) el uso de Gherkin del primer estilo.

Estos dos estilos de Gherkin que menciono aquí son en cierto modo dos extremos. Asimismo, si bien la sintáxis de Gherkin no viene sufriendo grandes cambios, la forma de uso ha ido variando dado pie a distintos estilos. Lo importante para mi es que cada equipo establezca explícitamente un conjunto de convenciones sobre su uso que estén en sintonía con el uso que le vayan a dar.

Para aquellos que apuesten a utilizar Gherkin como herramienta de colaboración/especificación les recomiendo la serie de libros de Rose y Nagy «The BDD Books«.

Cucumber modo dual

Cucumber es la «herramienta insignia» de BDD. Permite escribir ejemplos (pruebas) ejecutables utilizando Gherkin, una sintaxis amistosa para gente no técnica.

Una de las particularidades de Cucumber es que provee una muy buena experiencia para el desarrollador pues tiene la capacidad de instanciar dentro del mismo proceso la ejecución de la pruebas y la aplicación a probar incluso cuando la aplicación bajo prueba es una aplicación web[*]. Esto tiene algunas implicancias interesantes como ser:

  1. Las pruebas corren mucho más rápido (comparado a si corrieran pruebas y aplicación en procesos separados) lo cual se traduce un feedback más rápido para el desarrollador
  2. Es posible desde las pruebas acceder el estado interno de la aplicación simplificando las tareas de setup (Given/Arrange) y verificación (Then/Assert) de las pruebas

Es importante tener presente que estas dos cualidades pueden traer también algunas complicaciones (pruebas inconsistentes, resultados incorrectos, etc) sino no se toman ciertas precauciones al momento de automatización de las pruebas.

Resulta interesante tener presente que si escribimos nuestras pruebas con cierta precaución en el uso de 2 (o sea: si evitamos acceder el estado interno de la aplicación por fuera de su interface pública) podemos entonces utilizar el mismo set de pruebas para probar la aplicación ya instalada en un ambiente de prueba.

Tenemos entonces dos usos posibles de Cucumber: 

  1. Como herramienta del programador, utilizándola en su máquina local para guiar su desarrollo y obtener feedback instantáneo de la ejecución de los ejemplos acordados con el usuario
  2. Como herramienta para ejecutar las pruebas de aceptación y regresión en un ambiente de prueba, siendo estas pruebas las mismas que el programador utilizo para guiar su desarrollo

Si bien en este caso estoy hablando de Cucumber (ruby) estas cuestiones que menciono también aplican a otros frameworks/tecnologías.

Mientras termino de escribir estas línea me doy cuenta que para entender mejor este tema puede resultar muy conveniente ver algo de código, por ello en los próximos días estaré publicando un video mostrando ejemplos de código.

[*] esto en parte tiene que ver con capacidades de Cucumber pero también con las abstracciones y capacidades de otras herramientas del stack de Ruby como Rack.

Selección de herramientas de prueba (parte 2)

En la primera entrega de esta serie presenté una arquitectura de prueba de referencia. En esta entrega voy a instanciar esa arquitectura de referencia en Ruby.

Comenzado por las pruebas del programador (unitarias y de componentes), tendremos que nuestro objeto de prueba serán clases programadas en Ruby. Para hacer este tipo de pruebas una de las herramientas más populares es RSpec la cual nos daría una arquitectura de pruebas conformada por:

  • Lenguaje de especificación: Lenguaje de Dominio Específico provisto por RSpec
  • Intérprete: es el propio RSpec
  • Driver: dado que estamos testeando clases Ruby, que estamos haciendo pruebas unitarias y de componentes y que RSpec está hecho en Ruby no requerimos de ningún driver particular. En todo caso nuestro driver es el mismo Ruby
  • Librería de aserciones: es el mismo RSpec
  • Motor de ejecución: RSpec

Un detalle a tener presente es que este tipo de pruebas son pruebas que hace el programador para sí mismo, ya sea para guiar el desarrollo (si está haciendo TDD) o bien para garantizar que sus clases se comportan acorde a lo esperado. Este tipo de pruebas no suelen interesar al usuario, pues son de índole técnica y al mismo tiempo mucho más granulares que las funcionalidades pedidas por el usuario.

arquitectura_developer_tests_ruby

 

 

Pasando ahora a las pruebas de aceptación funcionales, si bien podríamos utilizar el mismo set de herramientas, la realidad es que esperamos que el usuario esté involucrado y por ello usaremos un set distinto de herramientas de cara tener un lenguaje de especificación más «amistoso» para el usuario. Al mismo tiempo el objeto de nuestras pruebas ya no son clases sino la aplicación como un todo:

  • Lenguaje de especificación: Gherkin
  • Intérprete: Cucumber
  • Driver: dependerá de la forma en que pretendamos comunicarnos con la aplicación. Suponiendo que nos comunicamos via HTTP, podríamos utilizar Capybara en conjunto con WebDriver de Selenium.
  • Librería de aserciones: RSpec
  • Motor de ejecución: Cucumber

arquitectura_user_tests_ruby

 

Breve historia de las herramientas BDD

Uno de los mayores referentes en lo que a BDD respecta es Dan North. Hacia el año 2003 Dan se encontraba trabajando en ThoughtWorks cuando creo JBehave. Según el mismo cuenta, JBehave comenzó como un experimento para ver como podría haber sido JUnit si se hubiera concebido desde un principio como una herramienta para hacer TDD en lugar de un mero framework de pruebas automatizadas. Fue por aquel entonces que el dominio jbehave.org fue registrado. Con el correr del tiempo JBehave fue evolucionando con foco en la automatización de pruebas de aceptación.

Tiempo más tarde algunas ideas de JBehave fueron incorporadas por Dave Astels en RSpec.

Hacia 2007 Dan North inspirado por el la tracción generada por RSpec y con la intención de proveer una forma simple y elegante de describir comportamiento a nivel de aplicación publica RBehave, el cual trae como novedad la posibilidad de especificar comportamiento en texto plano, idea que luego daría origen a lo que en la actualidad se conoce como sintaxis Gherkin.

Al poco tiempo David Chelimsky incorpora en RSpec el soporte de especificaciones en texto plano.

En 2008 Liz Keogh and Mauro Taveli reescriben completamente desde cero JBehave haciendo uso de algunas característica recientemente incorporadas en Java 5 y utilizando JUnit 4 como runtime. Entre otras cosas incorporan soporte para especificaciones en texto plano e integración con Maven. El resultado es JBehave 2.

Por aquel entonces Aslak Hellesoy ya se encontraba trabajando en un reemplazo para el ejecutor de especificaciones en texto plano de RSpec, el cual sería bautizado como Cucumber. La primera versión de Cucumber fue liberada hacia fines del 2008.

Mi percepción es que Cucumber marcó un punto de inflexión en la difusión de BDD. Fue precisamente en el contexto de Cucumber que se estandarizó la sintaxis de especificación en texto plano y se la bautizó como Gherkin.

Fue tal el impacto de Cucumber, que en la actualidad existen herramientas de BDD con soporte para Gherkin implementadas en diversos lenguajes, algunas de ellas son directamente reimplementaciones de Cucumber sobre otras plataformas (tal es el caso de Cucumber-JVM).

Fuentes:

 

Primeros pasos con Cucumber-JVM

Hace poco más de dos meses estoy trabajando casi cotidianamente con Cucumber-JVM y como consecuencia de ello he aprendido muchísimo sobre dicha herramienta. Si bien ya tenía bastante experiencia con la implementación Cucumber-Ruby, resulta que la implementación Java tiene sus particularidades.

Para compartir lo que he ido aprendiendo he decidido grabar una serie de videos. He aquí el primero de ellos: Introducción a Cucumber-JVM, espero les resulte útil.

 

Cucumber: teoría y práctica

Teoría: Cucumber es una herramienta para hacer BDD y como tal logró su difusión. BDD una de las técnicas de la familia Test-First.

Práctica: uno puede utilizar Cucumber sin hacer BDD ni Test-First. O sea, es posible usar Cucumber para escribir pruebas sobre aplicaciones ya existentes.

Personalmente hace más de dos años que usé Cucumber por primera vez y desde entonces he leído bastante sobre al respecto y en la gran mayoría de los casos se lo trata como una herramienta de BDD. Sin embargo casi toda la gente que conozco que usa Cucumber (y que no es mucha) no lo usa para hacer BDD, sino que lo utiliza como una herramienta tradicional de automatización de tests.

*nota: al decir Cucumber, hablo de forma genérica, sin centrarme en ninguna implementación particular.

cucumber_logo

 

 

Los dilemas del uso de Gherkin/Cucumber

Como mencioné anteriormente, estoy trabajando un proyecto ocupando el rol de Software Engineer in Test (SET). Una de las primeras cuestiones que debí resolver en el proyecto fue acordar con los analistas/testers la convenciones para escribir los tests con Gherkin. Todo aquel que haya trabajado con Gherkin en algún momento seguramente se ha enfrentado a los diversos dilemas que plantea el uso de la herramienta.

Dilema 1

  • Escribir steps con parámetros, que permiten un alto reuso de steps definitions disminuyendo la cantidad necesaria de glue-code
  • Escribir steps sin parámetros, que permiten un mayor nivel de abstracción y expresividad, pero que al mismo tiempo requieren de más glue-code

Dilema 2

  • Agrupar los steps definitions por feature, lo cual posiblemente genere muchos archivos de steps definitions y también repetición de glue-code, pero que facilita la trazabilidad entre los archivos de features y los archivos de steps definitions
  • Agrupar los steps definitions por concepto de negocio, lo cual ayuda a reducir la cantidad de archivos y la posible repetición de código, pero que puede dificultar la trazabilidad entre archivos de features y archivos de steps definitions

Dilema 3

  • Hacer steps «stateless» (que no dependen de datos de contexto) lo cual permite mayor reuso, pero que obliga a pasar la información de contexto como parámetro de los steps
  • Hacer steps «stateful» (que sí dependen de datos del contexto) lo cual permite generar steps más claros yreducir/omitir los parámetros en los steps, pero que disminuye el reuso de steps y obliga a generar código adicional para el manejo del contexto

En todos los dilemas planteados existen más alternativas que las mencionadas, de hecho las alternativas aquí planteadas son en cierto modo extremas pero sirven para ejemplificar algunas de las cuestiones que debemos definir al trabajar con esta herramienta.

Al mismo tiempo hay algunas cuestiones adicionales a considerar dependiendo de la implementación de cucumber que utilicemos. Por ejemplo: la implementación Ruby es un DSL que permite fácilmente compartir estado entre los steps definitions independientemente de la forma en que los agrupemos, mientras que en la implementación Java (Cucumber-JVM) sólo es posible (en principio) compartir estado entre los steps definitions que esten agrupados en la misma clase.

Todas estas cosas son las que estuve definiendo la primer semana de proyecto. Ahora estoy trabajando en generar un «micro framework» (básicamente algunas clases helpers) que me faciliten la implementación de los steps definitions.

Continuará…