Reprocessed, by Matt Patterson

Something approaching a weblog

Setting up a more-integrated Selenium, Cucumber, and Rails

I've been using [Selenium] [1] and [Cucumber] [2] together for a few months now, and I've really enjoyed being able to actually test the Ajaxy parts of my apps. The initial setup was a bit of a pain, and most of the tutorials I found advocated completely separate and parallel Selenium and non-Selenium Cucumber feature directories. I didn't want this, worked out a more integrated way, which changes to Cucumber have since allowed me to make even more straightforward. I'm going to assume you want to set up Selenium with Cucumber, or want to improve your setup, rather than introduce either thing.

The basic setup

Installing Cucumber and Selenium is a simple matter these days. You can follow the first couple of paragraphs from the Setting Up Selenium wiki page, which essentially amount to:

  1. Install the selenium-client gem
  2. Make really sure you're using the right version of selenium-client for your version of Webrat
  3. Tweak your Cucumber config appropriately

I can't emphasise enough how important that second point is. If your selenium-client and Webrat gems aren't bang on you'll get the worst kind of total, baffling, failure. A simple point-update to selenium-client will be enough to throw them out. You have been warned. The cucumber wiki page above usually has the correct version numbers, although it seems to be out of date. selenium-client 1.2.17 works with webrat 0.5.3 for me.

The less basic setup

What I wanted was a way to pick out certain Scenarios from a Feature to run through Selenium to run, and not to also run those through Rails integration tests. Cucumber's tags provide a good mechanism for that, but don't solve the problem of needing to use an entirely different setup and Webrat mode. After much fiddling, I managed to get a pretty sane setup by tweaking the standard Rake tasks and using Cucumber profiles.

To cut a long story short, the selenium profile sets an environment variable that we pick up in support/env.rb and use to set the right Webrat mode, and whatever other tweaks are needed. Ben Mabey's database_cleaner gem provides a way to keep your test DB under control, since you can't meaningfully use transactions in your steps with Selenium as the app under test is running as a completely different process, and won't see anything done in any transactions you do use...

With that done, you then use the @selenium tag to mark Scenarios (or whole Features, if you want) which ought to be run with Selenium. The standard profile is set to ignore things tagged @selenium, and the selenium profile is set to ignore everything else.

To run them, I set up more Rake tasks. You can either tweak the Rake tasks Cucumber comes with to add Selenium tasks and make the standard tasks invoke the right profiles, or add new ones. I chose to tweak the standard ones so I didn't keep accidentally running the Selenium-specific Scenarios.

I've created an example rails app on GitHub, at http://github.com/fidothe/cucumber_selenium_example. Looking at that's probably the easiest way to figure out how to make it work since you have to tweak in several places. The whistlestop tour looks like this:

Cucumber profiles

The Cucumber profiles (in rails_app/cucumber.yml) I used look like this:

default:      --color --tags ~@selenium,~@wip        --format pretty
wip:          --color --tags ~@selenium,@wip:2 --wip --format pretty
selenium:     --color --tags @selenium,~@wip         --format pretty cucumber_mode=selenium
selenium_wip: --color --tags @selenium,@wip:2  --wip --format pretty cucumber_mode=selenium

It provides a default profile which ignores the @selenium and @wip tags, a WIP profile which ignores @selenium WIP features, but runs non-selenium ones, and Selenium versions of those, which ignore things which aren't tagged@selenium.

The cucumber_mode environment variable is used later to pick up that we've been asked to run with Selenium, because all the tags do is affect which Scenarios and Features are presented to be run, rather than the environment they're run in.

features/support/env.rb

I test for which mode (:rails or :selenium) Webrat should use by checking the cucumber_mode environment variable:

case ENV["cucumber_mode"]
when "selenium"
  webrat_mode = :selenium
  db_clean_strategy = :truncation
else
  webrat_mode = :rails
  db_clean_strategy = :transaction
end

I also decide which database cleaning strategy Database Cleaner should use here, using DB truncation for Selenium, and the faster transaction method with Rails' integration testing.

Scenario tagging

The final piece is to actually mark some scenarios for running with Selenium. This is just a matter of tagging the Scenario (or entire Feature) with the @selenium tag. A trivial example:

Feature: Example
  In order to demonstrate using Selenium
  As the developer
  I want one of these Scenarios to invoke Selenium, and the other not to.

  @selenium
  Scenario: Via Selenium
    When I go to the homepage
    Then I should be on the homepage

  Scenario: Not via Selenium
    When I go to the homepage
    Then I should be on the homepage

The end

Have a look at the example Rails app I put together, which is on GitHub at http://github.com/fidothe/cucumber_selenium_example. That app puts all the pieces together, and you can look back over the history to see exactly what I had to change.

Not forgetting:

This page is: