Story Runner - constructive ideas 16 Oct 2007
Yesterday I ranted on some criticisms that I have of RSpec’s Story Runner. This post will try to follow up the criticisms which an implementation I like.

The code we started with yesterday is:

Story "View Home Page", %{
  As a user I want to view my home page
  So that I can get a birds eye view of the system
}, :type => RailsStory do

  Scenario "Latest published article" do
    Given "an author named", "Zach" do |name|
       @user = User.create! :name => name

    When "he visits the home page", "/" do |path|
       get path

    Then "he should see the latest article published front and center" do
       # ... do something to see the article shows as expeted


Here is a version of this that I like, and is implemented in test/unit story runner (which has yet to make its public debut):

Story "As a user I want to view my home page so that I can get a birds eye view of the system"

Scenario "Latest published article" do
  Given "an author"
  When "he visits the home page"
  Then "he will see the latest article published front and center"

Story Description

It gets rid of the unnecessary description and :type parameters. It leaves only the story itself.

Story Do/End Blocks

It removes them. They are not needed. They provide unnecessary grouping which hurts readability for the customer.

Scenario Do/End Blocks

These are kept. These are the one area where I feel making a visual grouping is important for the customer and the developer. All Scenario’s will be attached to the Story declaration which precedes it.

Argument Passing

I removed the arguments from the original example. They weren’t providing value to my tests. I do believe argument passing is valuable but it must be in a way that adds value to the test. For example in a game I am ok with the 500_000 below:

  Scenario "Becoming the top scorer" do
    Given "a player"
    When "he makes a guess of", 500_000
    Then "he will see himself as the number one scorer"

It seems that a lot of cases where arbitrary arguments are passed can be updated to make their descriptions more meaningful. It can do this by describing what is under test based on what may influence the test. Typically a user’s name doesn’t do this. So rather then:

Given "a user named Zach"

I’d rather see why “Zach” is special. Maybe he’s an admin:

Given "an administrator"

The Process

I haven’t explained yet how test/unit story runner is implemented so bare with me. When you add a new Story or Scenario it looks like the example posted above.

When you change or update an existing Story or Scenario it still looks like the example posted above. There is no embedded code, or horrendous blocks following every declaration (just the Scenario one!).

Reusable Code

Here’s where a high level overview of test/unit story runner comes to fruition. It takes a story part’s (Given/When/Then/And) description and turns that into a helper method using a simple mapping process.

  • take the description and downcase it
  • strip out all punctuation
  • then replace spaces with underscores
  • look for the method name
  • if it exists, execute it, otherwise strip the first word and repeat

So every story description maps to a method. And these methods can be included at the bottom of your story file or in a shared helper file (or both!).

Here’s a few example mappings:

Given "an administrator" # => an_administrator
When "he clicks the big button" # => clicks_the_big_button
Then "he will see a picture"  # => see_a_picture
And "he will see another button!" # => see_another_button

def an_administrator
  # ...

def clicks_the_big_button
  # ...

def see_a_picture
  # ...

def see_another_button
  # ...

What I like about this is that it is easy for developers to map a description to a method, and it separates the implementation of a Story from it’s definition. This allows the customers to work consistently to add and updates Story’s, as well as the same for developers.

Final Thoughts

One thing that lacks right now in the test/unit story runner is a way to mark Story’s as pending. I think this can be accomplished though by capitalizing the Story, Scenario or any of the story parts.

Story "Logging in"

SCENARIO "User with good credentials" do
  # this whole scenario is pending regardless of whats in it
  Given "..."
  When "..."

Scenario "User with bad credentials" do
  # this whole scenario is executed

STORY "Logging out" # marks the whole story as pending 

Scenario "logged in user" do
  # this will be marked as pending because of the story it belongs to

There has been recent discussion on the RSpec mailing list about this:

  • “”:
  • “”:

It seems that everyone is in agreement that the holy grail is a pure plain text file. There are drawbacks with this though that are currently being evaluated.

Pat Maddox has came up with a nice alternative to my above suggestion called SpecMatcher’s. It looks promising to the users of rspec though that Story Runner will turn into something truly beneficial to developers and customers alike.

blog comments powered by Disqus