Leftshift’s Weblog

Techniques to improve your code

Plain Text Story Runner using RSpec

At work we have started looking at the use of automated accpetance tests to provide validation of our development efforts. There are quite a few tools out there that can help with this; FitNesse, RSpec, NSpec, Selenium, Watir and WatiN to name a few.

So far we have found most success with RSpec and Watir. RSpec allows us to create plain text stories and acceptance criteria and run these as part of our integration builds. The slight problem we have is that we are mainly a .NET shop so using Ruby poses a few problems with regards to experience and knowledge. Unfortunately NSpec [which incorporates NBehave] doesn’t currently have a plain text story runner. Anyway here’s how you go about setting up RSpec in three simple steps.

Step 1: Write your story and acceptance criteria and save in a plain text file

Step 2: Create the story interpreter

Step 3: Create the runner

So without further ado I’ll explain each step in detail:

Step 1Write your story and acceptance criteria

Fire up your favourite text editor and type in your story and acceptance criteria. The format used is the same as Dan North describes in his BDD article here.

Here is a sample story and criteria:

Story: filter files based on file extension
 As a user
 I want to filter files based on file extension
 So that I can select only the files I am interested in

 Scenario: filter matches a file
 Given the file abc.jpg   
 When user filters on the extension .jpg
 Then user should see abc.jpg in the results

You may [and probably will] have multiple acceptance criteria. The acceptance criteria can contain multiple Givens, Whens and Thens connected together using the And operator at the start of the line. I’ve saved this as filter_story.txt

Ok, so far so good. Onto…

Step 2Create the story interpreter

This is where you hook up the plain text to the code being tested.

steps_for(:filter) do
  Given("the file $file1") do |file1|
    #setup
  end
  When("I filter on the extension $extension") do |extension|
    #do the work
  end
  Then("the result should contain $file1") do |file1|
    #test assertion
  end
end

As you can see the code format matches the acceptance criteria quite closely. You use the $ notation to pick up variables from the text. I haven’t actually implemented any code in the Given, When and Then section but as you can see you setup in the Given, do the work in the When and perform the test assertion in the Then block. Save this with a sensible name [filter_steps.rb in this case]. All we need now is something to run it…

Step 3Create the runner

The last piece of the puzzle is creating the runner, like so:

require 'rubygems'
require 'spec/story'
require 'filter_steps.rb'

with_steps_for :filter do
  run 'filter_story.txt'
end

This makes sure Ruby knows about your steps and runs the story.

The Output

Running the sample above gave me the following output:

>ruby story_filter.rb
Running 1 scenarios

Story: filter files based on extension

  As a user
  I want to filter files based on extension
  So that I can select only the files I am interested in

  Scenario: filter matches a file

    Given the file abc.jpg

    When I filter on the extension .jpg

    Then the result should contain abc.jpg

1 scenarios: 1 succeeded, 0 failed, 0 pending
>Exit code: 0

Hope this was of some use, as the information out there is a little bit sketchy at the moment. It took me a couple of hours to figure this out, but my Ruby is a bit thin on the ground!

Advertisements

26 June 2008 - Posted by | Automation | , , ,

No comments yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: