¡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!




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