Reuse Given Scenarios with GivenScenario in RSpec StoryRunner

Update: I’ve made some improvements upon this using WebRat that you can read all about here.

I’m having a great time with RSpec‘s new StoryRunner, and wanted to share a bit of what you can do with it. I learn very well from examples and I know many others do as well so I hope that this provides as a useful resource for getting your ideas flowing on how to use StoryRunner for your own projects.

To start, I set up a simple project using the following

# Create the project
rails blog
cd blog

# Install Restful Authentication
script/plugin install
script/generate authenticated user sessions

# Create a Post model
script/generate scaffold post title:string body:text published:boolean

Now I have the essentials for a simple app to play with. Of course you’ll also want to install RSpec.

Now to try out StoryRunner! I’ll write a story to cover the login system. No need to write Spec’s for it, it’s already well tested. However in writing this story we get a reusable Scenario that will be nice to have later, as well as give us a nice starting point for stories.

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

Story "User Stuff", %{
  As a writer
  I want to be a member
  So that I can make posts
}, :type => RailsStory do
  Scenario "I log in as a new user" do

    Given("that I am not logged in") do
      delete "/sessions/destroy"
      controller.should_not be_logged_in
      @user_count = User.count

    When("I create a new user", "Glenn") do |name|
      @user = User.create(:login => name, 
            :email => "#{name}", :password => name, 
            :password_confirmation => name)

    Then("there should be 1 more user stored") do
      User.count.should == @user_count + 1

    When("I login as", "Glenn") do |name|
     post "/sessions/create", :login => name, :password => name
    Then("I should be logged in") do
      controller.should be_logged_in
      response.should be_redirect

This should be pretty easy to follow. I start by logging out, then creating a user and logging in with it. This is runnable with ruby stories/user_story.rb

Now that this is already written, we can easily add similar stories. Notice that we don’t have to re-define our steps here:

Scenario "I log in with the wrong user/password" do

  Given "that I am not logged in"
  When "I create a new user", "Glenn"
  Then "there should be 1 more user stored"
  When "I login as", "JoeBob"
  Then "I should not be logged in" do
    controller.should_not be_logged_in
    response.should_not be_redirect

Now I’ll write a story for our Posts. Notice the usage of “GivenScenario”, very cool!

Scenario "Create a new post while logged in" do

  GivenScenario "I log in as a new user"

  Then "I should be able to make a post" do
    post "/posts/create", :post => {:title => "the title", 
            :body => "The body!"}
    @post = Post.find_by_title("the title")
    flash[:notice].should == "Post was successfully created."
    @post.should_not be_nil
    response.should be_redirect

What if I want to also test posts failing? I’ll modify the previous step to be a bit more robust (and also more useful).

Then "I $should be able to make a post", "should" do |should|
  post "/posts/create", :post => {:title => "my title", 
            :body => "The body!"}
  @post = Post.find_by_title("my title")
  if should == "should"
    flash[:notice].should == "Post was successfully created."
    @post.should_not be_nil
    flash[:notice].should be_nil
    @post.should be_nil
  response.should be_redirect

With this change, I can easily add a new Scenario to test when making posts should fail:

Scenario "Create a new post while not logged in" do
  Given "that I am not logged in" 
  Then "I $should be able to make a post", "should not"

I hope you found this useful!


#1 Andy Watts on 04.29.08 at 11:09 am

First time I’ve seen GivenScenario.
Re-usable, nested, scenarios!
Thanks for this.

#2 Glenn Ford on 04.29.08 at 11:49 am

I’m glad you enjoyed it!