Cucumber is a BDD tool that aids in outside-in development by executing plain-text features/stories as automated acceptance tests. Written in conjunction with the stakeholder, these Cucumber “features” clearly articulate business value and also serve as a practical guide throughout the development process: by explicitly outlining the expected outcomes of various scenarios developers know both where to begin and when they are finished. I will present the basic usage of Cucumber, primarily in the context of web applications, which will include a survey of the common tools used for simulated and automated browser-testing. Common questions and pitfalls that arise will also be discussed.
1 of 137
Downloaded 741 times
More Related Content
Outside-In Development With Cucumber
1. Outside-In
Development
With
Ben Mabey
Saturday, March 14, 2009
13. * not executed
* documentation value
Feature: title * variant of contextra
* business value up front
In order to [Business Value]
As a [Role]
I want to [Some Action] (feature)
Saturday, March 14, 2009
14. There is no template.
What is important to
have in narrative:
* business value
* stakeholder role
* user role
* action to be taken by
user
Saturday, March 14, 2009
15. Scenario: title
Given [Context]
When I do [Action]
Then I should see [Outcome]
Saturday, March 14, 2009
16. Scenario: title
Given [Context]
And [More Context]
When I do [Action]
And [Other Action]
Then I should see [Outcome]
But I should not see [Outcome]
Saturday, March 14, 2009
17. project_root/
|
`-- features
Saturday, March 14, 2009
18. project_root/
|
`-- features
|-- awesomeness.feature
|-- greatest_ever.feature
Saturday, March 14, 2009
19. project_root/
|
`-- features
|-- awesomeness.feature
|-- greatest_ever.feature
`-- support
|-- env.rb
`-- other_helpers.rb
Saturday, March 14, 2009
20. project_root/
|
`-- features
|-- awesomeness.feature
|-- greatest_ever.feature
`-- support
|-- env.rb
`-- other_helpers.rb
|-- step_definitions
| |-- domain_concept_A.rb
| `-- domain_concept_B.rb
Saturday, March 14, 2009
41. features/manage_my_wishes.feature
Feature: manage my wishes
In order to get more stuff
As a greedy person
I want to manage my wish list for my family members to view
Scenario: add wish
Given I am logged in
When I make a quot;New carquot; wish
Then quot;New carquot; should appear on my wish list
Saturday, March 14, 2009
47. features/i_can_has_wishez_plz.feature
OH HAI: I CAN HAS WISHEZ PLZ
SO DAT I CAN HAS HUGS TIEM AN PLAY TIEM AN SLEEP TIEM AN NOM TIEM
AS NICE GREEDEE KITTEH
I CAN PLZ MANEGE TEH WISHEZ
MISHUN: MAK NEW WISH
GIVN I AM LOGGD IN
WEN I MAK quot;CHEEZBURGERquot; WISH
DEN I C quot;CHEEZBURGERquot; ON ME WISHEZ LIT
Saturday, March 14, 2009
55. features/step_definitions/user_steps.rb
Given /^I am logged in$/ do
@current_user = create_user(:email_confirmed => true)
end
Test Data Builder / Object Mother
Saturday, March 14, 2009
56. features/step_definitions/user_steps.rb
Given /^I am logged in$/ do
@current_user = create_user(:email_confirmed => true)
end
Fixture Replacement, Fixjour, Factory Girl, etc
spec/fixjour_builders.rb
Fixjour do
define_builder(User) do |klass, overrides|
klass.new(
:email => quot;user#{counter(:user)}@email.comquot;,
:password => 'password',
:password_confirmation => 'password'
)
end
end
Saturday, March 14, 2009
58. features/step_definitions/user_steps.rb
Given /^I am logged in$/ do
@current_user = create_user(:email_confirmed => true)
visit new_session_path
fill_in quot;Emailquot;, :with => @current_user.email
fill_in quot;Passwordquot;, :with => valid_user_attributes[quot;passwordquot;]
click_button
end
Saturday, March 14, 2009
59. features/step_definitions/user_steps.rb
Webrat / Awesomeness
Given /^I am logged in$/ do
@current_user = create_user(:email_confirmed => true)
visit new_session_path
fill_in quot;Emailquot;, :with => @current_user.email
fill_in quot;Passwordquot;, :with => valid_user_attributes[quot;passwordquot;]
click_button
end
Saturday, March 14, 2009
60. features/step_definitions/user_steps.rb
Webrat / Awesomeness
Given /^I am logged in$/ do
@current_user = create_user(:email_confirmed => true)
visit new_session_path
fill_in quot;Emailquot;, :with => @current_user.email
fill_in quot;Passwordquot;, :with => valid_user_attributes[quot;passwordquot;]
click_button
end
Saturday, March 14, 2009
61. features/step_definitions/user_steps.rb
Webrat / Awesomeness
Given /^I am logged in$/ do
@current_user = create_user(:email_confirmed => true)
visit new_session_path
fill_in quot;Emailquot;, :with => @current_user.email
fill_in quot;Passwordquot;, :with => valid_user_attributes[quot;passwordquot;]
click_button
end
features/support/env.rb
require 'webrat'
Webrat.configure do |config|
config.mode = :rails
end
Saturday, March 14, 2009
62. features/step_definitions/user_steps.rb
Webrat / Awesomeness
Given /^I am logged in$/ do
@current_user = create_user(:email_confirmed => true)
visit new_session_path
fill_in quot;Emailquot;, :with => @current_user.email
fill_in quot;Passwordquot;, :with => valid_user_attributes[quot;passwordquot;]
click_button
end
features/support/env.rb
require 'webrat'
Webrat.configure do |config|
config.mode = :rails
end
Adapter
Saturday, March 14, 2009
63. features/step_definitions/user_steps.rb
Webrat / Awesomeness
Given /^I am logged in$/ do
@current_user = create_user(:email_confirmed => true)
visit new_session_path
fill_in quot;Emailquot;, :with => @current_user.email
fill_in quot;Passwordquot;, :with => valid_user_attributes[quot;passwordquot;]
click_button
end
features/step_definitions/webrat_steps.rb
When /^I press quot;(.*)quot;$/ do |button|
click_button(button)
end
19 Steps Out-of-box
When /^I follow quot;(.*)quot;$/ do |link|
click_link(link)
end
When /^I fill in quot;(.*)quot; with quot;(.*)quot;$/ do |field, value|
fill_in(field, :with => value)
end
Saturday, March 14, 2009
64. features/step_definitions/user_steps.rb
Given /^I am logged in$/ do
@current_user = create_user(:email_confirmed => true)
visit new_session_path
fill_in quot;Emailquot;, :with => @current_user.email
fill_in quot;Passwordquot;, :with => valid_user_attributes[quot;passwordquot;]
click_button
end
Saturday, March 14, 2009
65. features/step_definitions/user_steps.rb
Given /^I am logged in$/ do
@current_user = create_user(:email_confirmed => true)
visit new_session_path
fill_in quot;Emailquot;, :with => @current_user.email
fill_in quot;Passwordquot;, :with => valid_user_attributes[quot;passwordquot;]
click_button
# make sure we have actually logged in- so we fail fast if not
session[:user_id].should == @current_user.id
end
Saturday, March 14, 2009
66. features/step_definitions/user_steps.rb
Given /^I am logged in$/ do
@current_user = create_user(:email_confirmed => true)
visit new_session_path
fill_in quot;Emailquot;, :with => @current_user.email
fill_in quot;Passwordquot;, :with => valid_user_attributes[quot;passwordquot;]
click_button
# make sure we have actually logged in- so we fail fast if not
session[:user_id].should == @current_user.id
controller.current_user.should == @current_user
end
Saturday, March 14, 2009
67. features/step_definitions/user_steps.rb
Given /^I am logged in$/ do
@current_user = create_user(:email_confirmed => true)
visit new_session_path
Specify outcome, not implementation.
fill_in quot;Emailquot;, :with => @current_user.email
fill_in quot;Passwordquot;, :with => valid_user_attributes[quot;passwordquot;]
click_button
# make sure we have actually logged in- so we fail fast if not
session[:user_id].should == @current_user.id
controller.current_user.should == @current_user
response.should contain(quot;Signed in successfullyquot;)
end
Saturday, March 14, 2009
68. features/step_definitions/user_steps.rb
Given /^I am logged in$/ do
@current_user = create_user(:email_confirmed => true)
visit new_session_path
fill_in quot;Emailquot;, :with => @current_user.email
fill_in quot;Passwordquot;, :with => valid_user_attributes[quot;passwordquot;]
click_button
# make sure we have actually logged in- so we fail fast if not
response.should contain(quot;Signed in successfullyquot;)
end
Saturday, March 14, 2009
74. features/step_definitions/wish_steps.rb
When /^I make a quot;(.+)quot; wish$/ do |wish|
end
Then /^(.+) should appear on my wish list$/ do |wish|
end
Saturday, March 14, 2009
75. features/step_definitions/wish_steps.rb
When /^I make a quot;(.+)quot; wish$/ do |wish|
end
Then /^(.+) should appear on my wish list$/ do |wish|
end
Regexp Capture -> Yielded Variable
Saturday, March 14, 2009
76. features/step_definitions/wish_steps.rb
When /^I make a quot;(.+)quot; wish$/ do |wish|
visit quot;/wishesquot;
click_link quot;Make a wishquot;
fill_in quot;Wishquot;, :with => wish
click_button
end
Then /^(.+) should appear on my wish list$/ do |wish|
end
Saturday, March 14, 2009
77. features/step_definitions/wish_steps.rb
When /^I make a quot;(.+)quot; wish$/ do |wish|
visit quot;/wishesquot;
click_link quot;Make a wishquot;
fill_in quot;Wishquot;, :with => wish
click_button
end
Then /^(.+) should appear on my wish list$/ do |wish|
response.should contain(quot;Your wish has been added!quot;)
response.should contain(wish)
end
Saturday, March 14, 2009
78. features/step_definitions/wish_steps.rb
When /^I make a quot;(.+)quot; wish$/ do |wish|
visit quot;/wishesquot;
click_link quot;Make a wishquot;
fill_in quot;Wishquot;, :with => wish
click_button
end
No route matches “/wishes” with
{:method=>:get} appear on my wish list$/ do |wish|
Then /^(.+) should (ActionController::RoutingError)
response.should contain(quot;Your wish has been added!quot;)
response.should contain(wish)
end
Saturday, March 14, 2009
79. config/routes.rb
ActionController::Routing::Routes.draw do |map|
map.resources :wishes
Saturday, March 14, 2009
80. config/routes.rb
ActionController::Routing::Routes.draw do |map|
map.resources :wishes
When I make a “New car” wish
uninitialized constant WishesController (NameError)
Saturday, March 14, 2009
81. config/routes.rb
ActionController::Routing::Routes.draw do |map|
map.resources :wishes
$./script generate rspec_controller new create
Saturday, March 14, 2009
82. config/routes.rb
ActionController::Routing::Routes.draw do |map|
map.resources :wishes
When I make a “New car” wish
Could not find link with text or title or
id “Make a wish” (Webrat::NotFoundError)
Saturday, March 14, 2009
84. app/views/wishes/index.html.erb
<%= link_to quot;Make a wishquot;, new_wish_path %>
When I make a “New car” wish
Could not find field: “Wish” (Webrat::NotFoundError)
Saturday, March 14, 2009
85. features/step_definitions/wish_steps.rb
When /^I make a quot;(.+)quot; wish$/ do |wish|
visit quot;/wishesquot;
click_link quot;Make a wishquot;
fill_in quot;Wishquot;, :with => wish
click_button
end
Saturday, March 14, 2009
86. features/step_definitions/wish_steps.rb
When /^I make a quot;(.+)quot; wish$/ do |wish|
visit quot;/wishesquot;
click_link quot;Make a wishquot;
fill_in quot;Wishquot;, :with => wish
click_button
end
app/views/wishes/new.html.erb
<% form_for :wish do |f| %>
<%= f.label :name, quot;Wishquot; %>
<%= f.text_field :name %>
<%= submit_tag quot;Make the wish!quot; %>
<% end %>
Saturday, March 14, 2009
87. features/step_definitions/wish_steps.rb
When /^I make a quot;(.+)quot; wish$/ do |wish|
visit quot;/wishesquot;
click_link quot;Make a wishquot;
fill_in quot;Wishquot;, :with => wish
click_button
end
Location Strategy FTW!
app/views/wishes/new.html.erb
<% form_for :wish do |f| %>
<%= f.label :name, quot;Wishquot; %>
<%= f.text_field :name %>
<%= submit_tag quot;Make the wish!quot; %>
<% end %>
Saturday, March 14, 2009
90. spec/controllers/wishes_controller_spec.rb
describe WishesController do
describe quot;POST / (#create)quot; do
it quot;should create a new wish for the user with the paramsquot; do
user = mock_model(User, :wishes => mock(quot;wishes associationquot;))
controller.stub!(:current_user).and_return(user)
user.wishes.should_receive(:create).with(wish_params)
post :create, 'wish' => {'name' => 'Dog'}
end
end
end
Saturday, March 14, 2009
91. app/controllers/wishes_controller.rb
class WishesController < ApplicationController
def create
current_user.wishes.create(params['wish'])
end
end
Saturday, March 14, 2009
92. spec/controllers/wishes_controller_spec.rb
describe WishesController do
describe quot;POST / (#create)quot; do
before(:each) do
.....
it quot;should redirect the user to their wish listquot; do
do_post
response.should redirect_to(wishes_path)
end
end
end
Saturday, March 14, 2009
93. app/controllers/wishes_controller.rb
def create
current_user.wishes.create(params['wish'])
redirect_to :action => :index
end
Saturday, March 14, 2009
94. View
Controller
Model
Saturday, March 14, 2009
95. app/controllers/wishes_controller.rb
def create
current_user.wishes.create(params['wish'])
redirect_to :action => :index
end
When I make a “New car” wish
undefined method `wishes` for #<User:0x268e898>
(NoMethodError)
Saturday, March 14, 2009
96. app/controllers/wishes_controller.rb
def create
current_user.wishes.create(params['wish'])
redirect_to :action => :index
end
$./script generate rspec_model wish
name:string user_id:integer
Saturday, March 14, 2009
97. app/models/wish.rb
class Wish < ActiveRecord::Base
belongs_to :user
end
app/models/user.rb
class User < ActiveRecord::Base
include Clearance::App::Models::User
has_many :wishes
end
Saturday, March 14, 2009
98. app/models/wish.rb
class Wish < ActiveRecord::Base
belongs_to :user
end
app/models/user.rb
When I make a “New car” wish
Then “New <car” should appear on my wish
class User ActiveRecord::Base
include Clearance::App::Models::User
has_many the following element’s content to include
expected:wishes
“Your wish has been added!”
end
Saturday, March 14, 2009
99. spec/controllers/wishes_controller_spec.rb
it quot;should notify the user of creation via the flashquot; do
do_post
flash[:success].should == quot;Your wish has been added!quot;
end
Saturday, March 14, 2009
100. spec/controllers/wishes_controller_spec.rb
it quot;should notify the user of creation via the flashquot; do
do_post
flash[:success].should == quot;Your wish has been added!quot;
end
app/controllers/wishes_controller.rb
def create
current_user.wishes.create(params['wish'])
flash[:success] = quot;Your wish has been added!quot;
redirect_to :action => :index
end
Saturday, March 14, 2009
101. spec/controllers/wishes_controller_spec.rb
it quot;should notify the user of creation via the flashquot; do
do_post
flash[:success].should == quot;Your wish has been added!quot;
end
app/controllers/wishes_controller.rb
Then “New car” should appear on my wish
expected the following element’s content to include
def create
“New car”
current_user.wishes.create(params['wish'])
flash[:success] = quot;Your wish has been added!quot;
redirect_to :action => :index
end
Saturday, March 14, 2009
102. app/views/wishes/index.html.erb
<ul>
<% @wishes.each do |wish| %>
<li><%= wish.name %></li>
<% end %>
</ul>
Saturday, March 14, 2009
103. spec/controllers/wishes_controller_spec.rb
describe quot;GET / (#index)quot; do
def do_get
get :index
end
it quot;should assign the user's wishes to the viewquot; do
do_get
assigns[:wishes].should == @current_user.wishes
end
end
Saturday, March 14, 2009
107. Scenario: view members list
Given the following wishes exist
| Wish | Family Member |
| Laptop | Thomas |
| Nintendo Wii | Candace |
| CHEEZBURGER | FuzzBuzz |
When I view the wish list for quot;Candacequot;
Then I should see the following wishes
| Wish |
| Nintendo Wii |
Saturday, March 14, 2009
108. Given the following wishes exist
| Wish | Family Member |
| Laptop | Thomas |
| Nintendo Wii | Candace |
| CHEEZBURGER | FuzzBuzz |
features/step_definitions/wish_steps.rb
Given /^the following wishes exist$/ do |table|
end
end
Saturday, March 14, 2009
109. Given the following wishes exist
| Wish | Family Member |
| Laptop | Thomas |
| Nintendo Wii | Candace |
| CHEEZBURGER | FuzzBuzz |
features/step_definitions/wish_steps.rb
Given /^the following wishes exist$/ do |table|
table.hashes.each do |row|
member = User.find_by_name(row[quot;Family Memberquot;]) ||
create_user(:name => row[quot;Family Memberquot;])
member.wishes.create!(:name => row[quot;Wishquot;])
end
end
Saturday, March 14, 2009
110. Feature: Addition
In order to avoid silly mistakes
As a math idiot
I want to be told the sum of two numbers
Scenario Outline: Add two numbers
Given I have entered <input_1> into the calculator
And I have entered <input_2> into the calculator
When I press <button>
Then the result should be <output> on the screen
Examples:
| input_1 | input_2 | button | output |
| 20 | 30 | add | 50 |
|2 | 5 | add | 7 |
|0 | 40 | add | 40 |
Saturday, March 14, 2009
111. Feature: Addition
In order to avoid silly mistakes
As a math idiot
I want to be told the sum of two numbers
Scenario Outline: Add two numbers
Given I have entered <input_1> into the calculator
And I have entered <input_2> into the calculator
When I press <button>
Then the result should be <output> on the screen
Examples:
| input_1 | input_2 | button | output |
| 20 | 30 | add | 50 |
|2 | 5 | add | 7 |
|0 | 40 | add | 40 |
Saturday, March 14, 2009
112. Feature: Addition
In order to avoid silly mistakes
As a math idiot
I want to be told the sum of two numbers
Scenario Outline: Add two numbers
Given I have entered <input_1> into the calculator
And I have entered <input_2> into the calculator
When I press <button>
Then the result should be <output> on the screen
Examples:
| input_1 | input_2 | button | output |
| 20 | 30 | add | 50 |
|2 | 5 | add | 7 |
|0 | 40 | add | 40 |
Saturday, March 14, 2009
113. Feature: Addition
In order to avoid silly mistakes
As a math idiot
I want to be told the sum of two numbers
Scenario Outline: Add two numbers
Given I have entered <input_1> into the calculator
And I have entered <input_2> into the calculator
When I press <button>
Then the result should be <output> on the screen
Examples:
| input_1 | input_2 | button | output |
| 20 | 30 | add | 50 |
|2 | 5 | add | 7 |
|0 | 40 | add | 40 |
Saturday, March 14, 2009
114. Feature: Addition
In order to avoid silly mistakes
As a math idiot
I want to be told the sum of two numbers
Scenario Outline: Add two numbers
Given I have entered <input_1> into the calculator
And I have entered <input_2> into the calculator
When I press <button>
Then the result should be <output> on the screen
Examples:
| input_1 | input_2 | button | output |
| 20 | 30 | add | 50 |
|2 | 5 | add | 7 |
|0 | 40 | add | 40 |
Saturday, March 14, 2009
115. Steps Within Steps
When /^I view the wish list for quot;(.+)quot;$/ do |user_name|
Given quot;I am logged inquot;
visit quot;/wishes/#{user_name}quot;
end
Saturday, March 14, 2009
116. Steps Within Steps
When /^I view the wish list for quot;(.+)quot;$/ do |user_name|
Given quot;I am logged inquot;
visit quot;/wishes/#{user_name}quot;
end
Saturday, March 14, 2009
117. Helpers - Ruby FTW
When /^I view the wish list for quot;(.+)quot;$/ do |user_name|
login_as(create_user)
visit quot;/wishes/#{user_name}quot;
end
Saturday, March 14, 2009
118. Helpers - Ruby FTW
When /^I view the wish list for quot;(.+)quot;$/ do |user_name|
login_as(create_user)
visit quot;/wishes/#{user_name}quot;
end
Saturday, March 14, 2009
119. features/step_definitions/user_steps.rb
Given /^I am logged in$/ do
@current_user = create_user(:email_confirmed => true)
visit new_session_path
fill_in quot;Emailquot;, :with => @current_user.email
fill_in quot;Passwordquot;, :with => valid_user_attributes[quot;passwordquot;]
click_button
end
Saturday, March 14, 2009
120. features/step_definitions/user_steps.rb
def login_as(user)
visit new_session_path
fill_in quot;Emailquot;, :with => user.email
fill_in quot;Passwordquot;, :with => valid_user_attributes[quot;passwordquot;]
click_button
# make sure we have actually logged in- so we fail fast if not
response.should contain(quot;Signed in successfullyquot;)
end
Given /^I am logged in$/ do
@current_user = create_user(:email_confirmed => true)
login_as(@current_user)
end
Saturday, March 14, 2009
121. features/step_definitions/user_steps.rb
def login_as(user)
visit new_session_path
fill_in quot;Emailquot;, :with => user.email
fill_in quot;Passwordquot;, :with => valid_user_attributes[quot;passwordquot;]
click_button
# make sure we have actually logged in- so we fail fast if not
response.should contain(quot;Signed in successfullyquot;)
end
Given /^I am logged in$/ do
@current_user = create_user(:email_confirmed => true)
login_as(@current_user)
end
Saturday, March 14, 2009
122. features/step_definitions/user_steps.rb
def login_as(user)
visit new_session_path
fill_in quot;Emailquot;, :with => user.email
fill_in quot;Passwordquot;, :with => valid_user_attributes[quot;passwordquot;]
“Every time you monkeypatch
click_button
# make sure we have actually logged in- so we fail fast if not
Object, a kitten dies.”
response.should contain(quot;Signed in successfullyquot;)
end
Given /^I am logged in$/ do
@current_user = create_user(:email_confirmed => true)
login_as(@current_user)
end
http://seanohalpin.github.com/unobtrusive-metaprogramming/unobtrusive-metaprogramming.html
Saturday, March 14, 2009
123. features/step_definitions/user_steps.rb
module UserHelpers
def login_as(user)
visit new_session_path
fill_in quot;Emailquot;, :with => user.email
fill_in quot;Passwordquot;, :with => valid_user_attributes[quot;passwordquot;]
click_button
# make sure we have actually logged in- so we fail fast if not
response.should contain(quot;Signed in successfullyquot;)
end
end
Saturday, March 14, 2009
124. features/step_definitions/user_steps.rb
module UserHelpers
def login_as(user)
visit new_session_path
fill_in quot;Emailquot;, :with => user.email
fill_in quot;Passwordquot;, :with => valid_user_attributes[quot;passwordquot;]
click_button
# make sure we have actually logged in- so we fail fast if not
response.should contain(quot;Signed in successfullyquot;)
end
end
Saturday, March 14, 2009
125. features/step_definitions/user_steps.rb
module UserHelpers
def login_as(user)
visit new_session_path
fill_in quot;Emailquot;, :with => user.email
fill_in quot;Passwordquot;, :with => valid_user_attributes[quot;passwordquot;]
click_button
# make sure we have actually logged in- so we fail fast if not
response.should contain(quot;Signed in successfullyquot;)
end
end
World { |world| world.extend UserHelpers }
Saturday, March 14, 2009
126. I can skp teh
unit testz?
Saturday, March 14, 2009
127. Acceptance Tests Unit Tests
Application Level Object Level- Isolated!
For Customers For developers
Slow FAST! (should be at least)
Good confidence - Tighter Feedback Loop
Prevent against More about design!!!!!!!!!!!!
regression
Will need both gears!
Some things are easier to
test at the application
level and vice-versa.
Saturday, March 14, 2009
128. SRSLY?
Model specs,
Controller Specs,
and view specs!?
Saturday, March 14, 2009