Ejemplo de uso de Capybara, RSpec y Cucumber con una Aplicación Web Externa

Vamos a empezar testeando YouTube (no es que creamos que le haga falta :-). Esto es posible por que las pruebas que haremos son de aceptación: se refieren al comportamiento de la aplicación y por tanto no es necesario siquiera que tengamos su código fuente. Nos basta con tener la posibilidad de ejecutarla.

Para las pruebas usaremos Cucumber.

Cucumber es una herramienta para escribir acceptance tests. En la metodología TDD el stackholder/parte interesada en el negocio en vez de pasarle los requisitos al equipo de desarrollo, colabora con los desarrolladores en la escritura de las pruebas que expresan el resultado que el quiere.

Es por eso que a este tipo de pruebas se las denominan acceptance tests: intentan capturar lo que el stackholder quiere.

Estas pruebas son diferentes de los tests unitarios cuyo motivo es ayudar a los desarrolladores a comprobar su diseño software.

Se suele decir que los tests unitarios nos aseguran que construimos la cosa correctamente mientras que los tests de aceptación nos aseguran que construimos la cosa correcta.

Cuando se usa Behaviour-Driven Development BDD nos preocupamos de escribir los acceptance tests como ejemplos/examples que cualquiera pueda leer y entender.

Esto es lo que Cucumber intenta: hacer que la colaboración entre stackholders y desarrolladores sean fluída.

Este es un ejemplo de test de aceptación Cucumber:

Feature: Filling a Book Review

  Scenario: Complete Book Review
    Given I am on a book review site
    When I submit a book review
    Then I should see the saved details confirmed

Nótese como las pruebas son especificadas como examples/ejemplos de como se debe conducir el sistema en un escenario concreto.

Los ficheros de features están escritos en un lenguaje que se denomina Gherkin (pepinillo).

Cuando se usa cucumber lo primero es crear un fichero features en el que se guardan las pruebas.

Este será el scenario para testear YouTube en el fichero features/youtube_search.features:

$ cat youtube_search.feature 
Feature: Search for Videos on YouTube

  Scenario: Search for Videos of Large Rodents
    Given I am on the YouTube home page
    When I search for "capybara"
    Then videos of large rodents are returned

Esta es nuestra jerarquía de ficheros:

features/
 |-- youtube_search.feature
 |-- step_defs
 |  `-- steps.rb
 `-- support
      `-- env.rb

Supuesto que tenemos un fichero youtube_search.feature pero no hemos escrito aún las step definitions, si ejecutamos cucumber obtenemos:

[~/sinatra/sinatra-capybara/youtube]$ cucumber 
Feature: Search for Videos on YouTube

  Scenario: Search for Videos of Large Rodents # features/youtube_search.feature:3
    Given I am on the YouTube home page        # features/youtube_search.feature:4
    When I search for "capybara"               # features/youtube_search.feature:5
    Then videos of large rodents are returned  # features/youtube_search.feature:6

1 scenario (1 undefined)
3 steps (3 undefined)
0m0.003s

You can implement step definitions for undefined steps with these snippets:

Given(/^I am on the YouTube home page$/) do
  pending # express the regexp above with the code you wish you had
end

When(/^I search for "(.*?)"$/) do |arg1|
  pending # express the regexp above with the code you wish you had
end

Then(/^videos of large rodents are returned$/) do
  pending # express the regexp above with the code you wish you had
end
Copiamos y pegamos los fragmentos de código que nos aconseja Cucumber en nuestro fichero features/step_defs/steps.rb.

Ahora ejecutamos cucumber de nuevo:

~/sinatra/sinatra-capybara/youtube]$ tree
.
`-- features
    |-- step_defs
    |   `-- steps.rb
    |-- support
    |   `-- env.rb
    `-- youtube_search.feature

3 directories, 3 files

[~/sinatra/sinatra-capybara/youtube]$ cucumber 
Feature: Search for Videos on YouTube

  Scenario: Search for Videos of Large Rodents # features/youtube_search.feature:3
    Given I am on the YouTube home page        # features/step_defs/steps.rb:1
      TODO (Cucumber::Pending)
      features/youtube_search.feature:4:in `Given I am on the YouTube home page'
    When I search for "capybara"               # features/step_defs/steps.rb:5
    Then videos of large rodents are returned  # features/step_defs/steps.rb:9

1 scenario (1 pending)
3 steps (2 skipped, 1 pending)
0m0.003s

Cucumber nos informa que los pasos están por implementar.

Ahora editamos support/env.rb:

[~/sinatra/sinatra-capybara/youtube]$ cat features/support/env.rb 
require 'capybara/cucumber'

Capybara.default_driver = :selenium
Estamos usando Selenium WebDriver como herramienta para la automatización del navegador.

Completamos el fichero features/step_defs/steps.rb:

[~/sinatra/sinatra-capybara/youtube]$ cat features/step_defs/steps.rb 
Given(/^I am on the YouTube home page$/) do
  visit 'http://www.youtube.com'
end

When(/^I search for "(.*?)"$/) do |search_term|
  fill_in 'search_query', :with => search_term
  click_on 'search-btn'
end

Then(/^videos of large rodents are returned$/) do
  puts page.inspect
  expect(page).to have_content 'Garibaldi'
end
La llamada fill_in 'search_query', :with => search_term busca por un elemento del DOM que sea una text area o un text field con un atributo name, id o label establecido a search_query y lo rellena con el contenido de search_term (que es capybara).

Mirando el fuente de la página de YouTube encontramos el correspondiente tag input con el atributo name a search_query:

<input 
  id="masthead-search-term" autocomplete="off" autofocus
  class="search-term yt-uix-form-input-bidi" 
  name="search_query" value=""
  type="text" tabindex="1" title="Search">
Que tiene al lado el botón de búsqueda con un id igual a search-btn:
<button 
  class="yt-uix-button yt-uix-button-size-default yt-uix-button-default search-btn-component search-button" 
  type="submit" 
  onclick="
   if (_gel(&#39;masthead-search-term&#39;).value == &#39;&#39;) 
     return false; 
   _gel(&#39;masthead-search&#39;).submit(); 
   return false;;
   return true;
  " 
  id="search-btn" 
  tabindex="2" 
  dir="ltr"
>
<span class="yt-uix-button-content">Search </span>

A partir de la información search_query Capybara es capaz de encontrar el elemento <input> del DOM y rellenarlo.

Cuando ejecutamos de nuevo cucumber se abre Firefox que navega hasta YouTube y busca por Capybara comprobando que la página de resultados contiene la palabra Garibaldi y produciendo un informe como este:

[~/sinatra/sinatra-capybara/youtube]$ cucumber
Feature: Search for Videos on YouTube

  Scenario: Search for Videos of Large Rodents # features/youtube_search.feature:3
    Given I am on the YouTube home page        # features/step_defs/steps.rb:1
    When I search for "capybara"               # features/step_defs/steps.rb:5
    Then videos of large rodents are returned  # features/step_defs/steps.rb:10
      #<Capybara::Session>

1 scenario (1 passed)
3 steps (3 passed)
0m22.491s



Subsecciones
Casiano Rodriguez León 2015-01-07