With
Ben Mabey
Writing Software
not code
With
Ben Mabey
Writing Software
not code
With
Ben Mabey
Writing Software
not code
B
ehaviour
D
riven
D
evelopment
Tweet in the
blanks...
"most software projects are
like _ _ _ _ _ _ _ _"
"most software projects are
like _ _ _ _ _ _ _ _"
So... why are
software projects
like “The Homer”?
Feature
Devotion
Text
Placing emphasis on
features instead of
overall outcome
Shingeo Shingo of
Toyota says...
"Inspection to find
defects is waste."
"Inspection to
prevent defects is
essential."
"Inspection to find
defects is waste."
56% of all bugs are introduced in
Root Cause
Analysis
Popping the Why
Stack...
Protect Revenue
Increase Revenue
Manage Cost
Feature
: title
In order to
[Business Value]
As a
[Role]
I want to
[Some Action] (feature)
* not executed
* documentation value * variant of contextra * business value up front
There is no template.
What is important to have in narrative: * business value * stakeholder role * user role * action to be taken by userWith
Ben Mabey
Writing Software
not code
B
ehaviour
D
riven
D
evelopment
“All of these tools are great...
but, in the end, tools are tools.
While RSpec and Cucumber are
optimized for BDD, using them
doesn’t automatically mean
you’re doing BDD"
BDD is a
mindset
Feature
: title
In order to
[Business Value]
As a
[Role]
I want to
[Some Action] (feature)
* not executed
* documentation value * variant of contextra * business value up front
Scenario
: title
Given
[Context]
When I do
[Action]
Scenario
: title
Given [Context]
And
[More Context]
When I do [Action]
And
[Other Action]
Then I should see [Outcome]
project_root/
|
project_root/
|
`-- features
|-- awesomeness.feature
project_root/
|
`-- features
|-- awesomeness.feature
|-- greatest_ever.feature
`-- support
|-- env.rb
`-- other_helpers.rb
project_root/
|
`-- features
|-- awesomeness.feature
|-- greatest_ever.feature
`-- support
|-- env.rb
`-- other_helpers.rb
|-- step_definitions
| |-- domain_concept_A.rb
| `-- domain_concept_B.rb
Step
Step
Given
a widget
Given
#codes go here
/^a widget$/
do
end
Step
Given
a widget
Given
#codes go here
/^a widget$/
do
end
Definition
Step
Given
a widget
Given
#codes go here
/^a widget$/
do
end
Definition
a.feature
b.feature
a.feature
b.feature
a.feature
b.feature
a.feature
b.feature
28+
a.feature
b.feature
x_steps.rb
y_steps.rb
28+
Languages
a.feature
b.feature
x_steps.rb
y_steps.rb
RSpec, Test::Unit, etc
28+
a.feature
b.feature
x_steps.rb
y_steps.rb
Your Code
RSpec, Test::Unit, etc
28+
Write Code Example
(Unit Test)
REFACTOR
and
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
@proposed
Scenario: add wish
@proposed
Scenario: remove wish
@proposed
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 @wip
Scenario: add wish
Given I am logged in
When I make a "New car" wish
Then "New car" should appear on my wish list @proposed
Scenario: remove wish
@proposed
Scenario: tweet wish
Workflow
Workflow
git branch -b add_wish_tracker#
Workflow
git branch -b add_wish_tracker#
Tag Scenario or Feature with @wip
Workflow
git branch -b add_wish_tracker#
Tag Scenario or Feature with @wip
cucumber --wip --tags @wip
Workflow
git branch -b add_wish_tracker#
Tag Scenario or Feature with @wip
cucumber --wip --tags @wip
Develop it Outside-In
Workflow
git branch -b add_wish_tracker#
Tag Scenario or Feature with @wip
cucumber --wip --tags @wip
Develop it Outside-In
git rebase ---interactive; git merge
$ rake -T cucumber
$ rake -T cucumber
rake cucumber:ok OR rake cucumber
$ rake -T cucumber
rake cucumber:ok OR rake cucumber
cucumber --tags ~@wip --strict
$ rake -T cucumber
rake cucumber:ok OR rake cucumber
cucumber --tags ~@wip --strict
@wip on master?
@wip on master?
@wip on master?
$ rake -T cucumber
rake cucumber:wip
cucumber --tags @wip:2 --wip
@wip on master?
$ rake -T cucumber
rake cucumber:wip
cucumber --tags @wip:2 --wip
@wip on master?
$ rake -T cucumber
rake cucumber:wip
cucumber --tags @wip:2 --wip
@wip on master?
$ rake -T cucumber
rake cucumber:wip
Limit tags in flow
@wip on master?
$ rake -T cucumber
rake cucumber:all
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 @wip
Scenario: add wish
Given I am logged in
When I make a "New car" wish
Then "New car" should appear on my wish list @proposed
Scenario: remove wish
@proposed
Look Ma! backtraces!
features/step_definitions/user_steps.rb
Given /^I am logged in$/ do@current_user = create_user(:email_confirmed => true) end
features/step_definitions/user_steps.rb
Given /^I am logged in$/ do@current_user = create_user(:email_confirmed => true) end
features/step_definitions/user_steps.rb
Given /^I am logged in$/ do@current_user = create_user(:email_confirmed => true) end
spec/fixjour_builders.rb
Fixjour do
define_builder(User) do |klass, overrides| klass.new(
:email => "user#{counter(:user)}@email.com", :password => 'password',
:password_confirmation => 'password'
) end end
features/step_definitions/user_steps.rb
Given /^I am logged in$/ do@current_user = create_user(:email_confirmed => true) end
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 "Email", :with => @current_user.email
fill_in "Password", :with => valid_user_attributes["password"] click_button
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 "Email", :with => @current_user.email
fill_in "Password", :with => valid_user_attributes["password"] click_button
end
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 "Email", :with => @current_user.email
fill_in "Password", :with => valid_user_attributes["password"] click_button
end
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 "Email", :with => @current_user.email
fill_in "Password", :with => valid_user_attributes["password"] click_button
end
features/support/env.rb
require 'webrat'
Webrat.configure do |config| config.mode = :rails
end
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 "Email", :with => @current_user.email
fill_in "Password", :with => valid_user_attributes["password"] click_button
end
features/support/env.rb
require 'webrat'
Webrat.configure do |config| config.mode = :rails
end
Adapter
Webrat / Awesomeness
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 "Email", :with => @current_user.email
fill_in "Password", :with => valid_user_attributes["password"] click_button
end
features/step_definitions/webrat_steps.rb
When /^I press "(.*)"$/ do |button|click_button(button) end
When /^I follow "(.*)"$/ do |link| click_link(link)
end
When /^I fill in "(.*)" with "(.*)"$/ do |field, value| fill_in(field, :with => value)
end
When /^I select "(.*)" from "(.*)"$/ do |value, field| select(value, :from => field)
end
# Use this step in conjunction with Rail's datetime_select helper. For example:
# When I select "December 25, 2008 10:00" as the date and time
When /^I select "(.*)" as the date and time$/ do |time| select_datetime(time)
end
# Use this step when using multiple datetime_select helpers on a page or
# you want to specify which datetime to select. Given the following view:
# <%= f.label :preferred %><br />
# <%= f.datetime_select :preferred %> # <%= f.label :alternative %><br />
# <%= f.datetime_select :alternative %>
# The following steps would fill out the form:
# When I select "November 23, 2004 11:20" as the "Preferred" data and time
# And I select "November 25, 2004 10:30" as the "Alternative" data and time
When /^I select "(.*)" as the "(.*)" date and time$/ do |datetime, datetime_label|
select_datetime(datetime, :from => datetime_label) end
# Use this step in conjunction with Rail's time_select helper. For example:
# When I select "2:20PM" as the time
# Note: Rail's default time helper provides 24-hour time-- not 12 hour time. Webrat
# will convert the 2:20PM to 14:20 and then select it.
When /^I select "(.*)" as the time$/ do |time| select_time(time)
end
# Use this step when using multiple time_select helpers on a page or you want to
# specify the name of the time on the form. For example: # When I select "7:30AM" as the "Gym" time
When /^I select "(.*)" as the "(.*)" time$/ do |time, time_label| select_time(time, :from => time_label)
end
# Use this step in conjunction with Rail's date_select helper. For example:
# When I select "February 20, 1981" as the date
When /^I select "(.*)" as the date$/ do |date| select_date(date)
end
# Use this step when using multiple date_select helpers on one page or # you want to specify the name of the date on the form. For example: # When I select "April 26, 1982" as the "Date of Birth" date
When /^I select "(.*)" as the "(.*)" date$/ do |date, date_label| select_date(date, :from => date_label)
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
When /^I attach the file at "(.*)" to "(.*)" $/ do |path, field| attach_file(field, path)
end
Then /^I should see "(.*)"$/ do |text| response.should contain(text)
end
Then /^I should not see "(.*)"$/ do |text| response.should_not contain(text)
end
Then /^the "(.*)" checkbox should be checked$/ do |label| field_labeled(label).should be_checked
end
Webrat / Awesomeness
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 "Email", :with => @current_user.email
fill_in "Password", :with => valid_user_attributes["password"] click_button
Given /^I am logged in$/ do
@current_user = create_user(:email_confirmed => true) visit new_session_path
fill_in "Email", :with => @current_user.email
fill_in "Password", :with => valid_user_attributes["password"] click_button
end
features/step_definitions/user_steps.rb
Given /^I am logged in$/ do# make sure we have actually logged in- so we fail fast if not
session[:user_id].should == @current_user.id
features/step_definitions/user_steps.rb
Given /^I am logged in$/ do# make sure we have actually logged in- so we fail fast if not
controller.current_user.should == @current_user
end
Given /^I am logged in$/ do
@current_user = create_user(:email_confirmed => true) visit new_session_path
fill_in "Email", :with => @current_user.email
fill_in "Password", :with => valid_user_attributes["password"] click_button
Given /^I am logged in$/ do
@current_user = create_user(:email_confirmed => true) visit new_session_path
fill_in "Email", :with => @current_user.email
fill_in "Password", :with => valid_user_attributes["password"] click_button
features/step_definitions/user_steps.rb
Given /^I am logged in$/ do# 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("Signed in successfully")
end
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 "Email", :with => @current_user.email
fill_in "Password", :with => valid_user_attributes["password"] click_button
# make sure we have actually logged in- so we fail fast if not
response.should contain("Signed in successfully")
No route matches “/sessions/create” with
I’m going to cheat...
$ gem install thoughtbot-clearance
$ ./script generate clearance
Authlogic?
features/step_definitions/wish_steps.rb
When /^I make a "(.+)" wish$/ do |wish|end
Then /^(.+) should appear on my wish list$/ do |wish| end
features/step_definitions/wish_steps.rb
When /^I make a "(.+)" wish$/ do |wish|end
Then /^(.+) should appear on my wish list$/ do |wish| end
features/step_definitions/wish_steps.rb
When /^I make a "(.+)" wish$/ do |wish|visit "/wishes"
click_link "Make a wish"
fill_in "Wish", :with => wish click_button
end
Then /^(.+) should appear on my wish list$/ do |wish|
features/step_definitions/wish_steps.rb
Then /^(.+) should appear on my wish list$/ do |wish| response.should contain("Your wish has been added!") response.should contain(wish)
end
When /^I make a "(.+)" wish$/ do |wish| visit "/wishes"
click_link "Make a wish"
fill_in "Wish", :with => wish click_button
features/step_definitions/wish_steps.rb
When /^I make a "(.+)" wish$/ do |wish|visit "/wishes"
click_link "Make a wish"
fill_in "Wish", :with => wish click_button
end
Then /^(.+) should appear on my wish list$/ do |wish| response.should contain("Your wish has been added!") response.should contain(wish)
end
No route matches “/wishes” with
config/routes.rb
ActionController::Routing::Routes.draw do |map| map.resources :wishes
config/routes.rb
ActionController::Routing::Routes.draw do |map| map.resources :wishes
When I make a “New car” wish
config/routes.rb
ActionController::Routing::Routes.draw do |map| map.resources :wishes
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)
app/views/wishes/index.html.erb
app/views/wishes/index.html.erb
<%= link_to "Make a wish", new_wish_path %>
When I make a “New car” wish
features/step_definitions/wish_steps.rb
When /^I make a "(.+)" wish$/ do |wish|visit "/wishes"
click_link "Make a wish"
fill_in "Wish", :with => wish click_button
end
features/step_definitions/wish_steps.rb
When /^I make a "(.+)" wish$/ do |wish|visit "/wishes"
click_link "Make a wish"
fill_in "Wish", :with => wish click_button end
app/views/wishes/new.html.erb
<% form_for :wish do |f| %><%= f.label :name, "Wish" %>
<%= f.text_field :name %>
<%= submit_tag "Make the wish!" %> <% end %>
fill_in "Wish", :with => wish
<%= f.label :name, "Wish" %>
<%= f.text_field :name %>
features/step_definitions/wish_steps.rb
When /^I make a "(.+)" wish$/ do |wish|visit "/wishes"
click_link "Make a wish"
fill_in "Wish", :with => wish click_button end
app/views/wishes/new.html.erb
<% form_for :wish do |f| %><%= submit_tag "Make the wish!" %> <% end %>
View
spec/controllers/wishes_controller_spec.rb
describe WishesController dodescribe "POST / (#create)" do
end end
spec/controllers/wishes_controller_spec.rb
describe WishesController dodescribe "POST / (#create)" do
it "creates a new wish for the user with the params" do
user = mock_model(User, :wishes => mock("wishes association")) controller.stub!(:current_user).and_return(user)
user.wishes.should_receive(:create).with(wish_params) post :create, 'wish' => {'name' => 'Dog'}
end end end
app/controllers/wishes_controller.rb
class WishesController < ApplicationController
def create
current_user.wishes.create(params['wish']) end
spec/controllers/wishes_controller_spec.rb
describe WishesController dodescribe "POST / (#create)" do before(:each) do
...
end end
spec/controllers/wishes_controller_spec.rb
it "redirects the user to their wish list" do do_post
response.should redirect_to(wishes_path) end
app/controllers/wishes_controller.rb
def createcurrent_user.wishes.create(params['wish']) redirect_to :action => :index
View
Controller
Model
app/controllers/wishes_controller.rb
def createcurrent_user.wishes.create(params['wish']) redirect_to :action => :index
end
When I make a “New car” wish
undefined method `wishes` for #<User:0x268e898>
(NoMethodError)
app/controllers/wishes_controller.rb
def createcurrent_user.wishes.create(params['wish']) redirect_to :action => :index
end
$./script generate rspec_model wish
name:string user_id:integer
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
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
When I make a “New car” wish
Then “New car” should appear on my wish
expected the following element’s content to include
“Your wish has been added!”
spec/controllers/wishes_controller_spec.rb
it "notifies the user of creation via the flash" do do_post
flash[:success].should == "Your wish has been added!"
end
current_user.wishes.create(params['wish'])
redirect_to :action => :index
app/controllers/wishes_controller.rb
def create
flash[:success] = "Your wish has been added!"
end
spec/controllers/wishes_controller_spec.rb
it "notifies the user of creation via the flash" do do_post
flash[:success].should == "Your wish has been added!"
current_user.wishes.create(params['wish'])
redirect_to :action => :index
app/controllers/wishes_controller.rb
def create
flash[:success] = "Your wish has been added!"
end
spec/controllers/wishes_controller_spec.rb
it "should notifies the user of creation via the flash" do do_post
flash[:success].should == "Your wish has been added!"
end
Then “New car” should appear on my wish
expected the following element’s content to include
“New car”
app/views/wishes/index.html.erb
<ul>
<% @wishes.each do |wish| %>
<li><%= wish.name %></li>
<% end %>
spec/controllers/wishes_controller_spec.rb
describe "GET / (#index)" dodef do_get
get :index
end
it "assigns the user's wishes to the view" do do_get
assigns[:wishes].should == @current_user.wishes end
app/controllers/wishes_controller.rb
def index@wishes = current_user.wishes end
FAQ
How do I
test JS and
AJAX?
Slow
Slow
Fast
Integrated
Slow
Fast
Integrated
Slow
Fast
Integrated
Slow
Fast
Integrated
Slow
Fast
Integrated
Slow
Slow
Slow
Fast
Painful
Slow
Fast
Painful
Joyful
Celerity
Celerity
Celerity
Celerity
require "rubygems"
require "celerity"
browser = Celerity::Browser.new
browser.goto('http://www.google.com')
browser.text_field(:name, 'q').value = 'Celerity'
browser.button(:name, 'btnG').click
Culerity
require "rubygems"
require "culerity"
culerity_server = Culerity::run_server
browser = Culerity::RemoteBrowserProxy.new(culerity_server) browser.goto('http://www.google.com')
browser.text_field(:name, 'q').value = 'Celerity'
browser.button(:name, 'btnG').click
Celerity
+
+
HtmlUnit
CodeNote
Feature: CLI Server
In order to save me time and headaches As a presenter of code
I create a presentation in plaintext a'la Slidedown (from Pat Nakajima)
and have CodeNote serve it up for me
Scenario: basic presentation loading and viewing
Given that the codenote server is not running And a file named "presentation.md" with:
"""
!TITLE My Presentation !PRESENTER Ben Mabey
# This is the title slide !SLIDE
# This is second slide... """
When I run "codenote_load presentation.md"
And I run "codenote"
And I visit the servers address
For example of how to test CLI tools take a look at CodeNote on github. RSpec and Cucumber also have good examples of how to do this.
Feature: Twitter Quiz
In order to encourage audience participation where 90% of the audience is hacking on laptops
As a presenter I want audience members To answer certain questions via twitter
Feature: Twitter Quiz
In order to encourage audience participation where 90% of the audience is hacking on laptops
As a presenter I want audience members To answer certain questions via twitter
@proposed
Scenario: waiting for an answer
@proposed
Scenario: winner is displayed
@proposed
Scenario: fail whale
@proposed
Feature: Twitter Quiz
In order to encourage audience participation where 90% of the audience is hacking on laptops
As a presenter I want audience members To answer certain questions via twitter
@wip
@wip
Scenario: waiting for an answer
Given the following presentation """
!TITLE American History
!PRESENTER David McCullough # Wanna win a prize?
### You'll have to answer a question...
### in a tweet! First correct tweet wins! !SLIDE
# Who shot Alexander Hamilton?
## You must use #free_stuff in your tweet.
!DYNAMIC-SLIDE TwitterQuiz '#free_stuff "aaron burr"' !SLIDE
Okay, that was fun.
Lets actually start now. """
!DYNAMIC-SLIDE TwitterQuiz '#free_stuff "aaron burr"'
@wip
Scenario: waiting for an answer
Given the following presentation """
!TITLE American History
!PRESENTER David McCullough # Wanna win a prize?
### You'll have to answer a question...
### in a tweet! First correct tweet wins! !SLIDE
# Who shot Alexander Hamilton?
## You must use #free_stuff in your tweet.
!SLIDE
Okay, that was fun.
Lets actually start now. """
@wip
Scenario: waiting for an answer
Given the following presentation ...
And no tweets have been tweeted that match the '#free_stuff "aaron burr"' search When the presenter goes to the 3rd slide
And I go to the 3rd slide
Then I should see "And the winner is..."
Given /the following presentation$/ do |presentation|
end
Given the following presentation """
blah, blah """
Given /the following presentation$/ do |presentation|
end
Given the following presentation """
blah, blah """
CodeNote::PresentationLoader.setup(presentation)
Given /the following presentation$/ do |presentation|
end
Given the following presentation """
blah, blah """
And no tweets have been tweeted that match the '#free_stuff "aaron burr"' search
FAQ
How do I
test web
services?
http://github.com/chrisk/fakeweb
page = `curl -is http://www.google.com/`
FakeWeb.register_uri(:get, "http://www.google.com/", :response => page)
Net::HTTP.get(URI.parse("http://www.google.com/"))
Given %r{no tweets have been tweeted that match the '([']*)' search$} do |query|
end
And no tweets have been tweeted that match the '#free_stuff "aaron burr"' search
Given %r{no tweets have been tweeted that match the '([']*)' search$} do |query|
FakeWeb.register_uri(:get, search_url_for(query),
:body => canned_response_for(query))
end
And no tweets have been tweeted that match the '#free_stuff "aaron burr"' search
Given %r{no tweets have been tweeted that match the '([']*)' search$} do |query|
FakeWeb.register_uri(:get, search_url_for(query),
:body => canned_response_for(query))
Given %r{no tweets have been tweeted that match the '([']*)' search$} do |query|
FakeWeb.register_uri(:get, search_url_for(query),
:body => canned_response_for(query))
end
Given %r{no tweets have been tweeted that match the '([']*)' search$} do |query|
FakeWeb.register_uri(:get, search_url_for(query),
:body => canned_response_for(query))
end
def search_url_for(query)
"http://search.twitter.com/search.json?q=#{CGI.escape(query)}"
end
def canned_response_for(query) ....
return file_path end
Given %r{no tweets have been tweeted that match the '([']*)' search$} do |query|
FakeWeb.register_uri(:get, search_url_for(query),
:body => canned_response_for(query))
end
def search_url_for(query)
"http://search.twitter.com/search.json?q=#{CGI.escape(query)}"
end
def canned_response_for(query) ....
return file_path end
Given %r{no tweets have been tweeted that match the '([']*)' search$} do |query|
FakeWeb.register_uri(:get, search_url_for(query),
:body => canned_response_for(query))
end
def search_url_for(query)
"http://search.twitter.com/search.json?q=#{CGI.escape(query)}"
end
def canned_response_for(query) ....
return file_path end
Given %r{no tweets have been tweeted that match the '([']*)' search$} do |query|
FakeWeb.register_uri(:get, search_url_for(query),
:body => canned_response_for(query))
end
def search_url_for(query)
"http://search.twitter.com/search.json?q=#{CGI.escape(query)}"
end
def canned_response_for(query) ....
return file_path end
“Every time you monkeypatch
Object, a kitten dies.”
Given %r{no tweets have been tweeted that match the '([']*)' search$} do |query|
FakeWeb.register_uri(:get, search_url_for(query),
:body => canned_response_for(query))
end
module TwitterHelpers
def search_url_for(query)
"http://search.twitter.com/search.json?q=#{CGI.escape(query)}"
end
def canned_response_for(query) ....
return file_path end
Given %r{no tweets have been tweeted that match the '([']*)' search$} do |query|
FakeWeb.register_uri(:get, search_url_for(query),
:body => canned_response_for(query))
end
module TwitterHelpers
def search_url_for(query)
"http://search.twitter.com/search.json?q=#{CGI.escape(query)}"
end
def canned_response_for(query) ....
return file_path end
end
Given %r{no tweets have been tweeted that match the '([']*)' search$} do |query|
FakeWeb.register_uri(:get, search_url_for(query),
:body => canned_response_for(query))
end
module TwitterHelpers
def search_url_for(query)
"http://search.twitter.com/search.json?q=#{CGI.escape(query)}"
end
def canned_response_for(query) ....
return file_path end
end
When the presenter goes to the 3rd slide
When /the presenter goes to the
(\d+)(?:st|nd|rd|th) slide$/ do |slide_number| presenter_browser.goto path('/')
(slide_number.to_i - 1).times do
presenter_browser.link(:text, "Next").click end