Capybara with Given/When/Then steps in acceptance testing.
Many of us use cucumber to write acceptance tests.
We must to say it’s very nice tool that allows you to write features in English plain text.
The main goal is provide interface that should be clear for Customer(non-programmer) and Developer.
Cucumber example:
Feature: User signup
As a user
I want to sign in
So I can use service features
Background:
Given user with "jack@daniles.com" email and "qwerty" password
Scenario: Signing in with correct credentials
When I go to sign in page
And I fill in "email" with "jack@daniles.com"
And I fill in "password" with "qwerty"
And I click "Login" button
Then I should see "Welcome, jack@daniles.com!"
Scenario: User tries to sign in with incorrect password
When I go to sign in page
And I fill in "email" with "jack@daniles.com"
And I fill in "password" with "bla"
And I click "Login" button
Then I should see "Invalid credentials"It’s very readable. But cucumber has one big downside:
Steps maintenance
All steps are global and a instance variable from one step is accessible in another step.
In big project it can easy lead to mess.
Also you can read some rants about cucumber:
Previously we also use cucumber as tool for user stories.
Lately we found that our customers NEVER read cucumber features.
So do we need it anymore? Let’s check alternatives.
As you probably know, cucumber use internally Capybara for steps implementation. And there is even more – latest version of Capybara comes with a built in DSL for creating descriptive acceptance tests!
So we decided to switch to pure rspec+capybara testing.
We moved our features to spec/acceptance directory.
Let’s look at rspec+capybara example.
RSpec+Capybara example
$ cat spec/acceptance/signup_feature_spec.rb
feature "User signup" do
background do
@user = Factory(:user, :email => 'jack@daniles.com', :password => 'qwerty')
end
scenario "Signing in with correct credentials" do
page.visit "/sessions/new"
page.fill_in "email", :with => "jack@daniles.com"
page.fill_in "password", :with => "qwerty"
page.click_button "Login"
page.should have_content("Welcome, jack@daniles.com!")
end
scenario "User tries to sign in with incorrect password" do
page.visit "/sessions/new"
page.fill_in "email", :with => "jack@daniles.com"
page.fill_in "password", :with => "bla"
page.click_button "Login"
page.should have_content("Invalid credentials")
end
endThus approach has one big benefit:
Feature description and feature implementation are located together.
You don’t need anymore to jump between feature and step files to guess where is implementation for a step.
It’s nice but there are also two downsides.
First downsides is that RSpec documentation output will be too poor and it’s not enough informative:
$ rspec -fs -c spec/acceptance/signup_feature_spec.rb User signup Signing in with correct credentials User tries to sign in with incorrect password Finished in 13.55 seconds 2 examples, 0 failures
Second downside is that scenario does not have anymore exact boundary blocks or steps.
It makes harder to understand the scenario flow.
Real user story often has big scenarios and we realized that cucumber steps were cool.
There were two ways:
- switch back to cucumber
- go further and invent
Railswarians never go back :)
Our Solution
If you decided to switch from Cucumber to Capybara acceptance testing try rspec-example steps gem.
With this gem you may use Given/When/Then steps into rspec example!
RSpec+Capybara+Steps example
feature "User signup" do
background do
@user = Factory(:user, :email => 'jack@daniles.com', :password => 'qwerty')
end
Steps "Signing in with correct credentials" do
When "I go to sign in page" do
page.visit "/sessions/new"
end
And "I fill in right email" do
page.fill_in "email", :with => @user.email
end
And "I fill in right password" do
page.fill_in "password", :with => "qwerty"
end
And "I click login" do
page.click_button "Login"
end
Then "I should see greeting" do
page.should have_content("Welcome, #{@user.email}!")
end
end
Steps "User tries to sign in with incorrect password" do
When "I go to sign in page" do
page.visit "/sessions/new"
end
And "I fill in right email" do
page.fill_in "email", :with => @user.email
end
And "I fill in wrong password" do
page.fill_in "password", :with => "bla"
end
And "I click login" do
page.click_button "Login"
end
Then "I should see error" do
page.should have_content("Invalid credentials")
end
end
endAnd documentation output will be:
$ rspec -fs -c spec/acceptance/signup_feature_spec.rb
User signup
Signing in with correct credentials
When I go to sign in page
And I fill in right email
And I fill in right password
And I click login" do
Then I should see greeting
User tries to sign in with incorrect password
When I go to sign in page
And I fill in right email
And I fill in wrong password
And I click login
Then I should see error
Finished in 13.55 seconds
2 examples, 0 failuresNow it’s much better. Isn’t it? :)
RSpec Example Steps features
- Does not hack RSpec Example. It only extends it.
- Brings Given/When/Then/And/But steps support into rspec example block that allow you to better organize scenario
- Adds shared_steps DSL
- Adds ability to mark step as pending
- Reports with color about passed/pending/failed step into documentation formating output
Installation
$ gem install rspec-example_steps
Usage
See README for more details.
Just add to spec/spec_helper.rb
require 'rspec/example_steps'
And enjoy Given/When/Then step usage!
