La Coctelera

Apuntes prestados

En el parque somos mayoría

2 Octubre 2008

¡Hola amigo! ¡Hola Pepino! (jugando con Cucumber)

Intro y preámbulos

Todas las opiniones que había leído sobre Cucumber eran buenas, muy buenas. Y cuando el río suena, agua lleva... mi primer contacto con el substituto del RSpec Story Runner ha sido toda una gozada.

Utilizando como referencia su documentación oficial para uso con Rails me puse manos a la obra con un objetivo bastante sencillo: el clásico "Hola mundo", pero en su versión BDD y utilizando castellano en las especificaciones.

Antes de nada instalamos las dependencias que tiene Cucumber. Por un lado, como root, cuatro gemas:


 # gem install term-ansicolor treetop diff-lcs hpricot
 

Y por otro cuatro plugins dentro de nuestra aplicación, a la que cariñosamente llamaremos "Saludeitor":


 rails saludeitor
 cd saludeitor
 git clone git://github.com/aslakhellesoy/cucumber.git vendor/plugins/cucumber
 git clone git://github.com/brynary/webrat.git vendor/plugins/webrat
 git clone git://github.com/dchelimsky/rspec.git vendor/plugins/rspec
 git clone git://github.com/dchelimsky/rspec-rails.git vendor/plugins/rspec-rails

(Si tenemos nuestra aplicación en un repositorio de git podemos incorporar los plugins como submódulos cambiando git clone por git submodule add, tal y como proponen en la documentación oficial)

Acto seguido preparamos el proyecto para que admita la definición de features (o características, como finalmente han sido traducidas en la implementación actual) con la ayuda del generador de Cucumber:


 @nube01:~/src/saludeitor$ script/generate cucumber
       create  features/steps
       create  features/steps/env.rb
       create  features/steps/common_webrat.rb
       exists  lib/tasks
       create  lib/tasks/cucumber.rake
       create  script/cucumber
 

El fichero features/steps/common_webrat.rb implementa las interacciones de navegación típicas, pero las define para features escritas en inglés. No lleva mucho tiempo substituir dichas definiciones por sus equivalentes en castellano de tal forma que, por ejemplo:


 When /^I fill in "(.*)" with "(.*)"$/ do |field, value|
 

 When /^relleno el campo "(.*)" con "(.*)"$/ do |field, value|
 

(Este es mi common_webrat.rb en castellano, por si tienes prisa ;)

Cucumber "flow"

Bueno, ya lo tenemos todo listo para empezar a tirar líneas, eso sí, como esto es "BiDiDí" las tiraremos primero describiendo comportamiento... y como esto es Cucumber, lo haremos de tal forma que nuestra madre pueda participar también si le da por ahí. Creamos pues un archivo features/saludo.feature y escribimos algo como:


 Característica: Saludo
   Para sentirse como en su casa
   Un usuario
   Debería recibir un saludo
 
   Escenario: Saludo universal
     Cuando visito la portada
     Entonces debería ver "¡Hola amigo!"
 

Bien, ya tenemos una especificación. Si le pedimos a Cucumber que compruebe si nuestra aplicación la cumple tenemos muchas posibilidades de que no lo haga. Pero vamos a hacerlo para ver por qué se queja y comenzar a sentir el "flow" ;)

Para lanzar únicamente un fichero de features utilizamos script/cucumber, y para que las interprete en castellano le metemos el parámetro --language, tal que:


 $ script/cucumber --language es  features/saludo.feature
 

A lo que, si nuestra aplicación esta "pelá-y-mondá" como el script de Rails la trajo la mundo, Cucumber responderá:


 Característica: Saludo
   Para sentirse como en su casa
   Un usuario
   Debería recibir un saludo
 
   Escenario: Saludo universal
     Cuando visito la portada
       No route matches "/" with {:method=>:get} (ActionController::RoutingError)
       [...]
 1 step failed
 1 step skipped
 

Con colores muy chulos a la par que apropiados: el principio en verde, justo hasta "Cuando visito la portada" que aparece en rojo junto con el volcado del error, los pasos pendientes de implementar en amarillo (para los que al final nos ofrece una plantilla de su definición que facilite su implementación) y los que se ha saltado por un error en azul.

Además tiene el detalle de subrayar lo que son los valores de los parámetros que recibirá la función que implementa el paso (en este caso "la portada"), lo que nos permite distinguirlos fácilmente de la parte fija de sus definiciones ("Cuando visito").

Bien, pues vamos modificar nuestra aplicación para que salga todo verde verde... ¡tan verde como un pepino!

Editamos el config/routes.rb y descomentamos la línea:


 map.root :controller => "welcome"
 

Y acto seguido creamos dicho controlador con la acción index:


 $ script/generate controller Welcome index
 

Volvemos a lanzar nuestra feature y:


 $ script/cucumber --language es features/saludo.feature
 Característica: Saludo
   Para sentirse como en su casa
   Un usuario
   Debería recibir un saludo
 
   Escenario: Saludo universal
     Cuando visito la portada
     Entonces debería ver "¡Hola amigo!"
       expected: /¡Hola amigo!/m,
            got: "<h1>Welcome#index</h1>\n<p>Find me in app/views/welcome/index.html.erb </p>\n"
      [...]
 

¡Se resiste el bribón! Bien, ahora nos dice que no encuentra por ninguna parte "¡Hola amigo!". Editamos app/views/welcome/index.html.erb y cambiamos el contenido de su H1 por dicho saludo. Volvemos a lanzar Cucumber y...


 Característica: Saludo
   Para sentirse escuchado
   Un usuario
   Debería poder recibir un saludo
 
   Escenario: Saludo universal
     Cuando visito la portada
     Entonces debería ver "¡Hola amigo!"
 
 2 steps passed
 

¡todo verdecico! ¡cómo un pepino! (aunque pueda parecer lo contrario, dice Aslak que el nombre de Cucumber no significa nada y que se le ocurrió porque en el momento que comenzó a desarrollarlo su chica se estaba comiendo un sandwich de pepino).

Bien, vamos a meterle un poco más de chicha a la aplicación (lo que viene siento volver a iterar para comenzar a sentir el "flow"). Metemos un segundo escenario en saludo.feature:


   Escenario: Saludo personal
     Cuando visito la portada
     Y completo "Nombre" con "Pepino"
     Y pulso el botón "Enviar"
     Entonces debería ver "¡Hola Pepino!"
 

Y ahora Cucumber nos dice:


 Característica: Saludo
   Para sentirse escuchado
   Un usuario
   Debería poder recibir un saludo
 
   Escenario: Saludo universal
     Cuando visito la portada
     Entonces debería ver "¡Hola amigo!"
 
   Escenario: Saludo personal
     Cuando visito la portada
     Y completo "Nombre" con "Pepino"
       Could not find [Webrat::TextField, Webrat::TextareaField, Webrat::PasswordField]: "Nombre" (RuntimeError)
     [...]
       ./features/steps/common_webrat.rb:13:in `Y /^completo "(.*)" con "(.*)"$/'
       features/saludo.feature:12:in `Y completo "Nombre" con "Pepino"'
     Y pulso el botón "Enviar"
     Entonces debería ver "¡Hola Pepino!"
 
 3 steps passed
 1 steps failed
 2 steps skipped
 

No encuentra en el HTML de la página el campo "Nombre". De hecho no tiene tampoco el botón "Enviar". Añadimos a app/views/welcome/index.html.erb el siguiente formulario (sé que no es nada Rails, pero es sólo un ejemplo):


 <form action="/">
   <input type="text" name="Nombre" />
   <input type="submit" value="Enviar" />
 </form>
 

Lo lanzamos y ahora Cucumber se queja en la última línea, en la que decimos que deberíamos ver "¡Hola Pepino!". En este momento nuestra aplicación muestra siempre "¡Hola amigo!". Retocamos el H1 de app/views/welcome/index.html.erb y lo dejamos tal que:


 <h1>¡Hola <%= params[:Nombre] || 'amigo' %>!</h1>
 

Y ahora sí que sí, lo lanzamos y Cucumber nos dice que tenemos... ¡todo un pepino!:


 Característica: Saludo
   Para sentirse como en su casa
   Un usuario
   Debería recibir un saludo
 
   Escenario: Saludo universal
     Cuando visito la portada
     Entonces debería ver "¡Hola amigo!"
 
   Escenario: Saludo personal
     Cuando visito la portada
     Y completo "Nombre" con "Pepino"
     Y pulso el botón "Enviar"
     Entonces debería ver "¡Hola Pepino!"
 
 6 steps passed
 

Fit tables

Las Fit tables fueron inventadas allá por 2002 por (ni más ni menos que) Ward Cunningham y han sido implementadas en Cucumber para evitar la repetición de escenarios.

En nuestra aplicación podríamos, por ejemplo, añadir una Fit tables en el segundo escenario que agilizase la especificación de nuevos comportamientos. Simplemente añadimos después del escenario su tabla. En nuestro caso:


 Característica: Saludo
   Para sentirse como en su casa
   Un usuario
   Debería recibir un saludo
 
   Escenario: Saludo universal
     Cuando visito la portada
     Entonces debería ver "¡Hola amigo!"
 
   Escenario: Saludo personal
     Cuando visito la portada
     Y completo "Nombre" con "Pepino"
     Y pulso el botón "Enviar"
     Entonces debería ver "¡Hola Pepino!"
 
    | lugar      | campo     | valor  | botón  | mensaje       |
    | la portada | Nombre    | Nando  | Enviar | ¡Melenas!     |
 

La primera fila de la tabla sólo sirve para poner un nombre descriptivo a cada uno de los parámetros manejados en el escenario (qué son los que Cucumber nos ha subrayado al interpretarlo). Al ver esta tabla Cucumber volvería a comprobar los pasos del escenario pero pasándoles los valores indicados en la tabla en lugar de los utilizados en el mismo (y tal y como tenemos el Saludeitor en este momento fallaría porque a "Nando" le responde con "¡Hola Nando!" y no con "¡Melenas!").

Tuneando la tarea de rake

Por último, comentar que para que la tarea de rake cucumber interprete las features en castellano tenemos que modificar el lib/tasks/cucumber.rake añadiéndole en las cucumber_ops el parámetro --language tal y como hacemos al lanzarlo desde la línea de comandos. Quedaría algo como:


 ...
   t.cucumber_opts = "--format pretty --language es"
 ...
 

Esto es todo, suerte con él y consumirlo con moderación para que no os repita ;)

¡Salud!

servido por Fernando 7 comentarios compártelo

7 comentarios · Escribe aquí tu comentario

Fernando Blat

Fernando Blat dijo

Al final no me pude pasar porque vamos con retraso con las fotos, pero vamos, con este post casi no hacía ni falta.

Muchas gracias por compartirlo!

3 Octubre 2008 | 07:36 AM

Fernando García Samblas

Fernando García Samblas dijo

¡de nada tocayo!

claro, el post es justo lo que hemos hecho en La Paella (de hecho primero escribí el borrador del post para usarlo de apoyo).

un abrazo!

3 Octubre 2008 | 03:15 PM

demimismo

demimismo dijo

Muy bueno el tutorial, habrá que probarlo :-)

22 Octubre 2008 | 05:10 PM

Fernando García Samblas

Fernando García Samblas dijo

Gracias!

No dejes de hacerlo, es un vicio!

22 Octubre 2008 | 09:22 PM

Jean-Michel Garnier

Jean-Michel Garnier dijo

Muy bueno tutorial. cucumber rocks!

23 Octubre 2008 | 04:20 PM

Ancor Cruz

Ancor Cruz dijo

Muchas gracias por el tutorial.

12 Marzo 2009 | 01:39 PM

Fernando García Samblas

Fernando García Samblas dijo

A ti por leerlo!

A ver si me animo y presento la herramienta que estoy utilizando para agilizar mi escritura de "características" (features) de un tiempo para acá: mundo-pepino.

He preferido hacer de conejillo de indias usándolo un proyecto real antes de escribir sobre ella. Le faltan cosas aún (como las relaciones polimórficas que ya están en camino) pero creo que ya está lo suficientemente listo como para presentarlo "oficialmente".

Salu2!

12 Marzo 2009 | 02:41 PM

Escribe tu comentario


Sobre mí

Avatar de Fernando

Apuntes prestados

España
ver perfil »
contacto »

Blog semestral sólo más personal que experimental de Fernando García Samblas.

Algo hay en la belleza [*] que le impide pasar sin rozar.

[*] "La ruina del amo execrable. Crónicas de Thomas Covenant el Incrédulo." de Stephen R. Donaldson


Fotos

Fernando García Samblas todavía no ha subido ninguna foto.

¡Anímale a hacerlo!

Buscar

suscríbete

Selecciona el agregador que utilices para suscribirte a este blog (también puedes obtener la URL de los feeds):

¿Qué es esto?

Crea tu blog gratis en La Coctelera