cuke_logo

Cucumber is a powerful tool to write BDD tests. You can write your scenarios and definitions with common language on an outside-in approach.

A good (but little long) place to start learning it is at Cucumber backgrounder.

I will bring some quick steps.

Installing and configuring

Install the gem at your machine $ gem install cucumber

After creating your rails project, edit your Gemfile to add Cucumber:

1
2
3
4
5
group :development, :test do
  gem "rspec-rails", ">= 2.0.1"
  gem 'cucumber-rails'
  gem 'database_cleaner'
end

and setup it with $ bundle install and $ rails g cucumber:install.

Creating your tests

On your Rails project, Cucumber tests stay at features folder on .feature extension files.

You could create a contact.feature file and write some scenario like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
Feature: Contact me
    In order to get in touch
    A visitor
    Should send me a message by contact form
 
    Scenario: Sends a contact message
        Given I am on the contact page
        And I fill in "contact visitor name" with "John"
        And I fill in "contact visitor email" with "john@mail.me"
        And I fill in "contact subject" with "Hello"
        And I fill in "contact message" with "Great post!"
        When I press "Send message"
        Then page should have notice message "Your message was successfully delivered."

* Pay attention to indentation

The scenario is composed by many steps.
Which step is a validation or a more simple test.

When you run the tests at first time with command $ bundle exec cucumber, you will get many warnings saying that your steps are undefined.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
cucumber_test$ bundle exec cucumber
Using the default profile...
Feature: Contact me

(....)

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

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

Given /^I fill in "(.*?)" with "(.*?)"$/ do |arg1, arg2|
  pending # express the regexp above with the code you wish you had
end

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

Then /^page should have notice message "(.*?)"$/ do |arg1|
  pending # express the regexp above with the code you wish you had
end

The scenario that I bring as example is simple, and the steps are simple browser navigation that Capybara will reproduce to us automagicaly.
So, to define the steps and say how Capybara should handle the navigation, you create a navigation_steps.rb inside features/step_definitions/ folder.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "paths"))

Given /^I am on (.+)$/ do |page_name|
  visit path_to(page_name)
end

When /^I go to (.+)$/ do |page_name|
  visit path_to(page_name)
end

When /^I press "([^\"]*)"$/ do |button|
  click_button(button)
end

When /^I click "([^\"]*)"$/ do |link|
  click_link(link)
end

When /^I fill in "([^\"]*)" with "([^\"]*)"$/ do |field, value|
  fill_in(field.gsub(' ', '_'), :with => value)
end

When /^I fill in "([^\"]*)" for "([^\"]*)"$/ do |value, field|
  fill_in(field.gsub(' ', '_'), :with => value)
end

When /^I fill in the following:$/ do |fields|
  fields.rows_hash.each do |name, value|
    When %{I fill in "#{name}" with "#{value}"}
  end
end

When /^I select "([^\"]*)" from "([^\"]*)"$/ do |value, field|
  select(value, :from => field)
end

When /^I check "([^\"]*)"$/ do |field|
  check(field)
end

When /^I uncheck "([^\"]*)"$/ do |field|
  uncheck(field)
end

When /^I choose "([^\"]*)"$/ do |field|
  choose(field)
end

Then /^I should see "([^\"]*)"$/ do |text|
  page.should have_content(text)
end

Then /^I should see \/([^\/]*)\/$/ do |regexp|
  regexp = Regexp.new(regexp)
  page.should have_content(regexp)
end

Then /^I should not see "([^\"]*)"$/ do |text|
  page.should_not have_content(text)
end

Then /^I should not see \/([^\/]*)\/$/ do |regexp|
  regexp = Regexp.new(regexp)
  page.should_not have_content(regexp)
end

Then /^the "([^\"]*)" field should contain "([^\"]*)"$/ do |field, value|
  find_field(field).value.should =~ /#{value}/
end

Then /^the "([^\"]*)" field should not contain "([^\"]*)"$/ do |field, value|
  find_field(field).value.should_not =~ /#{value}/
end

Then /^the "([^\"]*)" checkbox should be checked$/ do |label|
  find_field(label).should be_checked
end

Then /^the "([^\"]*)" checkbox should not be checked$/ do |label|
  find_field(label).should_not be_checked
end

Then /^I should be on (.+)$/ do |page_name|
  current_path.should == path_to(page_name)
end

Then /^page should have (.+) message "([^\"]*)"$/ do |type, text|
  page.has_css?("p.#{type}", :text => text, :visible => true)
end

And a support file paths.rb inside features/support/ folder.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
module NavigationHelpers
  def path_to(page_name)
    case page_name
 
    when /the home\s?page/
      '/'
    else
      begin
        page_name =~ /the (.*) page/
        path_components = $1.split(/\s+/)
        self.send(path_components.push('path').join('_').to_sym)
      rescue Object => e
        raise "Can't find mapping from \"#{page_name}\" to a path.\n" +
          "Now, go and add a mapping in #{__FILE__}"
      end
    end
  end
end

World(NavigationHelpers)

Running again your tests with $ bundle exec cucumber, now you will see they fail.
And now, you can go to the code!!
Write your models, controllers, views and routes ;)

Here at my example, the step definitions are only simple navigations, but you could write more complex tests, to simplify scenarios, like When I fill contact form could have this definition:

1
2
3
4
5
6
7
8
When /^I fill contact form$/ do
  visit contact_form_path
  fill_in('contact_visitor_name', :with => "John")
  fill_in('contact_visitor_email', :with => "john@mail.me")
  fill_in('contact_subject', :with => "Hello")
  fill_in('contact_message', :with => "Great post! Bye.")
  click_button("Send message")
end

Have fun!

Read more:
Cucumber Backgrounder
Beginning Outside-In Rails Development with Cucumber and RSpec