lost password?

home
•  xaraya
•  rails +
•  django
•  webdev
•  xamp
•  musings

rss
Tag this page
   

» Blogs that link here
last modified: Oct 22, 2007
(first posted: Sep 25, 2007)
(6166 Reads)
keywords: rspec story runner
Permalink

Beginners Guide to Rspec on Story Runner

I wished for a step by step How To get started with Story Runner, and had to write it myself.

Story Runner is a nice behavioral integration "test" (er, uh, spec) tool that been incorporated into Rspec and can be used in Ruby on Rail projects. Its not officially released with Rspec but is available in trunk.

References:

http://rspec.rubyforge.org - Rspec home

http://rubyforge.org/pipermail/rspec-devel/2007-August/003756.html - David Chelimsky's announcement

http://evang.eli.st/blog/2007/9/1/user-stories-with-rspec-s-story-runner - good tutorial

http://www.nabble.com/Getting-Started-with-Story-Runner-t4485820.html - a helpful discussion thread

http://dannorth.net/whats-in-a-story - explains the structure of a "story"

http://pastie.caboo.se/92472 - a famous pastie (fame is relative :)

http://dannorth.net/2007/06/introducing-rbehave - where it all started

Installation

Assuming you already have a Rails app going, install Rspec from trunk:

$ ruby script/plugin install svn://rubyforge.org/var/svn/rspec/trunk/rspec
$ ruby script/plugin install svn://rubyforge.org/var/svn/rspec/trunk/rspec_on_rails
$ script/generate rspec

Create a directory in your web root named "stories/"

Create a file stories/story_helper.rb with the following:

ENV["RAILS_ENV"] = "test"
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
require 'spec/rails/story_adapter'

Each story script is an .rb file with the following as its first line:

require File.join(File.dirname(__FILE__), "story_helper") 

To run a story, just run it directly:

$ ruby story/mystory.rb 

Thinking

From Dan North's article, the general idea is you spec out a conversation about a feature or requirement. There's an action title, a text narrative, and then a set of scenarios. The scenario should be described in terms of Givens, Events and Outcomes (given/when/then...)

The structure is basically like this:

Title (one line describing the story)

Narrative:
As a [role]
I want [feature]
So that [benefit]

Acceptance Criteria: (presented as Scenarios)

Scenario 1: Title
Given [context]
  And [some more context]…
When  [event]
Then  [outcome]
  And [another outcome]…

Scenario 2: …

A very simple first story

The first thing I do when describing a site to someone is go to the home page, and being exploring from there. So, that seems like a legitimate first story to spec out. I create a file stories/anonymous_visitor.rb

require File.join(File.dirname(__FILE__), "story_helper")

Story "Anonymous visitor to the site", %{
  As an anonymous visitor
  I want to visit public pages
}, :type => RailsStory do
  
  Scenario "Starts at home page and drills down" do
    When "visiting home page" do
      get "/"
    end
    
    Then "user should see home page" do
      response.should render_template('base/home')
    end
 
    When "he clicks on the About link" do
get "/about"
    end
 
    Then "user should see the About page" do 
      response.should render_template('base/about')
    end 
  end
end

OK, maybe that's not so practical (and really should covered in the corresponding controller spec), at least you can make sure your stories are running and good to go. (And after all, Rails' default unit test is simply def test_truth; assert true; end)

But then I start building on that, either with more clauses, new scenarios, and/or starting another story file. What about a user logging in? then he should see different content. And when he creates a post, then... And... And, suddenly you're off and (story) running.

Parameterized Scenario Clauses 

You can reuse scenario clauses, and pass parameters. For example,

Scenario "savings account is in credit" do
    Given "my savings account balance is", 100 do |balance|
      @savings_account = Account.new(balance)
    end
    And "my cash account balance is", 10 do |balance|
      @cash_account = Account.new(balance)
    end

Then the next scenario can reuse the Given clauses with different arguments,

Scenario "savings account is overdrawn" do
    Given "my savings account balance is", -20
    And "my cash account balance is", 10

Going one step further (as of today Oct  22 '07), Rspec trunk introduces Plain Text Scenarios, which integrates parameters into the text. See http://blog.davidchelimsky.net/articles/2007/10/21/story-runner-in-plain-english  Doing so, stories become "block-less"-- it encourages you to put all your step clauses into separate Step Matcher methods, which means we can now eliminate all the quote and write truly plain text stories (!!). It's still experimental, but given the pace this stuff is moving, it may be mainstream by the time you read this!

Scenario: savings account is in credit
Given my savings account balance is 100
And my cash account balance is 10

Working with Stories

I've found that writing stories (and their multiple scenarios) helps me think through how things should work (and not work) on the site.

When I implement a scenario, the "Given" clauses often correspond to models, which leads me to write a model spec, and then implement the model methods.

And perhaps some actions (especially POSTs that affect models or the session), so I spec their views and controllers. And implement them.

Usually by then I've lost my context so I'll go back to the current story and see where I'm at in the scenario.

The last Given is often a GET action (which provides visual context for the upcoming When clause), so I go ahead and do its view and controller specs.

The "When" clause is the meat of the scenario. It is a controller action, and its spec gets done next, which often requires drilling down into other models, views, and helper methods.

Then, the "Then" clauses tend to assert the response in a view. They'll probably also call model methods to check for results. They get spec'd and implemented, and so on.

As the stories grow more complex, previously spec'ed scenarios might be stuffed into the Given section of the next story. At that point I write reusable helper methods, which pulls the preconditions together, such as

Given "a user is logged in and has created a post" do
user_is_logged_in_and_has_created_a_post
end

In stories, don't use mocks, stubs, or fixtures, as you want to exercise the whole stack. Instead I found the fixture_replacement plugin to be an extremely handy model factory.

One other thing, the clauses in a scenario may be in a specific order that makes sense from a story point of view, but not necessarily the order I want to implement. But (presently) story-runner just stops when it encounters a pending (unimplemented) clause. I've added this little method to my story_helper.rb

def skip_pending(text='')
puts " SKIP PENDING #{text}"
end

which might be used like this:

    And "a welcome message is sent to user" do
      skip_pending "email notifications"
    end

 

 

Beginners Guide to Rspec on Story Runner

Posted by: Scott Taylor on October 23, 2007 01:56 AM
Hey - Thanks for linking to my FixtureReplacement plugin (seems like only a few members in the rspec crowd know about it). Let me know if there is anything I can do to make it more Story friendly. I haven't started using the Rspec User Stories (I'll wait until it's in a stable release) - but thanks for your introduction.

#

Re: Beginners Guide to Rspec on Story Runner

Posted by: Scott Taylor on December 05, 2007 07:56 PM
Just to let you know, with FixtureReplacement, version 2, you can now set this in config/environment.rb: FixtureReplacement.excluded_envrionments = ["production", "foobar"] If you try to include the plugin in any other environment, an error will be raised. The plugin will work in any other environment. The default is just ["production"]

#

Post a new comment

: This is not spam

Name :