
And just to be nice, here is an English version of this tutorial…
Introduction
This tutorial will guide you through installing and configuring an acceptance test environment for PHP project. For this we use several ruby gems, including cucumber, webrat and selenium-client, and write our scripts as cucumber features. Here's an example:
Feature: Example Features
Scenario: I am connected and i visit my page
Given I am connected as "username password"
When I visit "home page"
Then I should see "Hello username"
The advantage of this syntax is that it remains very readable for the mere mortals, no need to delve into the ruby code to understand what the test is supposed to do.
Each "sentence" in this scenario refers to a step which is the user actions necessary to perform that action in the user interface.
The user actions can be triggered by the webrat and selenium-client libraries. Webrat is particularly suited to interactions not involving javascript while selenium-client will interpret them correctly.
Installation
For this example we will use a debian system.
Let's start by installing rubygems and the needed gems.
aptitude install rubygems
gem install rspec cucumber webrat selenium-client
To run the binaries packaged with gems from the command line you must add the path to these binaries to your PATH:
export PATH=$PATH:$HOME/.gem/ruby/1.8/bin
To use selenium, start a selenium-rc server (ie: remote control). Starting selenium server is done automatically by webrat, however the version used caused me many problems with recent versions of Firefox. I suggest you grab the latest stable version at the following address:
http://seleniumhq.org/download/ (1.0.3 au 28 juin 2010).
It is sufficient to replace the file ~/.gem/ruby/1.8/gems/webrat-0.7.1/vendor/selenium-server.jar with the version you just downloaded.
wget http://selenium.googlecode.com/files/selenium-remote-control-1.0.3.zip
unzip selenium-remote-control-1.0.3.zip
cp selenium-server-1.0.3/selenium-server.jar $GEM_HOME/webrat-0.7.1/vendor/selenium-server.jar
(Remplace $GEM_HOME by your gems installation directory.)
Voila, now we can start writing our scenarios.
Note for iceweasel: you must add the path to the folder containing the iceweasel application.ini file to your PATH otherwise
export PATH=$PATH:/usr/lib/iceweasel
Configuration
Now that the installation is complete, we will have to configure a web environment to run our tests. This environment can be your development environment, the approach is specific to each project so I will not dwell on this point.
Let's turn to the configuration of our test environment.
We will create a directory features in the root of your project. This directory has the following structure:
$: features % tree
.
├── fixtures
│ └── image.jpg
├── home.feature
├── step_definitions
│ ├── user_steps.rb
│ └── webrat_steps.rb
└── support
├── env.rb
├── paths.rb
└── selenium.rb
3 directories, 10 files
Let us see more precisely what role each of these directories is.
The root of the features will contain your cucumber "features", ie scenarios.
fixtures contains the files used in your scripts (eg an image to upload to a file field).
The step_definitions stores definitions of steps used in the scenarios. It will usually contain a file per action type, in our example we have two files:
webrat_steps: Common actions, this file can be found in the code of the webrat gem (templates/skeleton/step_definitions/webrat_steps.rb.erb). It contains, among other common helpers to any type of web project (visit a page, check that it contains a text or an html tag, ...)user_steps: the user actions (login, post a comment ...)
The support is the configuration directory, it must contain the following files:
env.rb, the webrat configuration filepath.rb, is the file that defines the correspondence between the named routes that you call and the actual URL to send to the browser (example: "home page" => "/")selenium.rb, is the file where you can refactor out the specific actions to selenium.
Webrat
By default webrat does not use selenium to control the browser but uses instead mechanize, here is a sample configuration file to be placed in the support/env.rb to specify the use of selenium: (Note in passing that the address of your test web environment will be configured via an environment variable for simplicities sake):
unless ENV['CUCUMBER_HOST']
raise 'You must set CUCUMBER_HOST environment variable with the name of your host used to run cucumber features'
end
# RSpec
require 'rspec/expectations'
# Webrat
require 'webrat'
require 'test/unit/assertions'
World(Test::Unit::Assertions)
Webrat.configure do |config|
#config.mode = :mechanize
config.mode = :selenium
config.application_framework = :external
config.application_address = ENV['CUCUMBER_HOST']
config.application_port = "80"
end
World do
session = Webrat::Session.new
session.extend(Webrat::Methods)
session.extend(Webrat::Matchers)
session
end
Named Routes
Next you must specify the routes that can be called in your tests and their text versions. Indeed, in cucumber scenarios , a literary correspondence will be much more readable than the raw URL. Here is an example configuration of these routes to be stored in the file support/path.rb :
module NavigationHelpers
def path_to(page_name)
case page_name
when /home/
'/'
when /accueil/
'/accueil'
when /login/
'/identification'
else
raise "Can't find mapping from \"#{page_name}\" to a path.\n" +
"Now, go and add a mapping in #{__FILE__}"
end
end
end
World(NavigationHelpers)
Helpers
In order to have the scenarios as clear and as understandable as possible, we will define some helpers.
All files in the support will be loaded by cucumber in our example we will add a file selenium.rb for actions related to selenium.
Here is the support/selenium.rb file:
def wait_for_ajax(timeout=50000, request_count = 1)
request_count.times do |index|
selenium.wait_for_ajax :javascript_framework => :jquery
end
end
def wait_for_gritter
selenium.wait_for_element "gritter-notice-wrapper"
end
You can do the same for other libraries you might need.
Writing scripts
The scenarios are defined by several cucumber keywords: Given, When, Then and And.
Givendefines the initial contextWhendefines user actionsThendefines an assertion, the expected resultAndis an association word to make the scenatio more readable, it has the same role as the keyword of the previous line.
Some actions will be called in several scenarios, hence the need for code refactoring.
For example, the action Given I am connected as "username password" will certainly be present in 75% of your tests.
To avoid repeating the X actions necessary to connect, we refactored that in the steps of the step_definitions directory.
Here is an example of steps for the following example:
Given /^I am connected as "(\w)\ (\w)"$/ do |login, password|
visit path_to "login"
fill_in 'login', :with => login
fill_in 'password', :with => password
click_button "login"
wait_for_ajax # if your form is submitted via ajax
end
Now in each of our tests we can call: Given I am connected as "MyUsername MyPassword".
Feature: Example Features
Scenario: I am connected and i visit my page
Given I am connected as "username password"
When I visit "home page"
Then I should see "Hello username"
Run !
Now that you've written your first scenario, go to the root of your project and run the following command:
CUCUMBER_HOST=YOUR_HOST cucumber
(Replace YOUR_HOST by your test enviroment address.)
You should see your browser run your script … hands free!
