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