I’ve been developing multiple systems using Rspec stories for a little while now. There are a lot of great resources to get you started with a taste of what you can do with stories. Some of the resources I found useful where:
http://blog.davidchelimsky.net/2008/6/16/slides-from-railsconf
http://www.benmabey.com/2008/02/04/rspec-plain-text-stories-webrat-chunky-bacon/
http://evang.eli.st/blog/2007/10/8/story-runner-top-to-bottom-screencast
However once I had understood the basic idea I struggled to find practical examples and general guidance on writing real stories. So I’ve collected some of the lessons I’ve learnt along the way with story examples taken from real systems and how I’ve improved them as I learnt. Most examples are from web based applications.
There are no absolutes in these lessons, they are general rules. If the customer is NASA and they want an extremely explicit and low level story then that is fine. Really stories are the customers expressions of requirements and each customer may want to express these in different ways.
On the other hand it is often you helping the customer take those first baby steps into using stories. They turn to you for guidance until they can stand on their own two feet.
Stories are not specs!
Specs are your unit tests, they’re fast and lean and always running, helping you refactor and develop your code. They describe the system at the object level (and dependent on how you mock/stub they do this in isolation). Stories are like integration tests, they document how your system works. They help you know that your system meets the customers requirements.
Your spec test is going to care about testing each action in your controller.
Your story is going to care about usage of the system cutting through models/controllers/views.
Hence specs tend to have a high coverage while stories cover only that which is important to the customer!
Some people do run their stories continuously like their specs (which is only really practical if you are using only Webrat). Generally I only run my stories before a commit and I also have them running on a continuous integration server (CruiseControl.rb) on every commit.
Keep the story goals as real values for the customer
Story: Search
As a website user
I want a search box
So that I can enter terms and click search
Why does the website user want to enter terms and click search? To find things!
Story: Search
As a website user
I want to search
So that I can find content on the website
This is where what David Chelimsky mentions as ‘popping the “why”? stack’ helps us keep focused on real customer values.
Stories should not be exhaustive
Story: Changing pages urls
As a website user
I want to change the URLs of pages
So that I can improve any bad urls
Scenario: valid url
Scenario: blank url
Scenario: symbol url
Scenario: character url
Scenario: numeric url
I find here we can get a bit more generic and concentrate of what is really important in this story. There are two different behaviours valid and invalid. (This does bring up the idea that stories can follow a behavioural or stateful approach, which I’ll leave as a discussion for another time)
Story: Changing page urls
As a website user
I want to change the URLs of pages
So that I can improve any bad urls
Scenario: invalid url
Given a title which is invalid in a url
When I save
Then it should show me an error message telling me why it was invalid
Scenario: valid url
Given a title which is valid in a url
When I save
Then it should not show me an error message
This serves as a good base for the client to add to and refine as their requirements evolve. I prefer the stories to facilitate discussion rather than being documentation which starts to happen when you get too exhaustive.
Stories should speak with the customers domain terminology
Ideally your stories should be written by the customer in their terminology. Even if you don’t have direct access to a customer try putting yourself in their shoes.
With web applications if you find yourself mentioning things like ‘database’, ‘views’, ‘controllers’, ‘models’ and ‘sql’ your writing like a programmer and not an average customer (Well that’s what I’ve generally found). In the previous example note how we saved a whole lot of work by saying:
'valid in a url'
Rather than defining explicitly the different rules for what is a valid url in the story. So using the domain terminology can help make your stories more concise.
Stories should not be too low level.
Story: Viewing homepage
As a logged in user
I want to view my homepage
So that I will see it
Scenario: login
Given I'm logged in
When I visit my homepage
Then I will see it
If it takes you longer to write the story than complete the task its too small. When you end up with a couple of stories which are too small general its a good idea to combine them to form a more reasonably sized story. In this case I took the Viewing homepage story and combined it with a set of small stories:
Story: Important pages are accessible
As a logged in user
I want to visit key pages in the site
So that I can successful navigate around it<span style="text-decoration: line-through;">
</span>
Scenario: Homepage
Scenario: Login page
Scenario: Payment page
Stories should not be too high level.
Stories that are too large are referred to as Epic stories. Epics tend to occur when the story scenarios are trying to be too exhaustive or the story is a:
Compound story – comprises multiple shorter stories
Complex story – Cannot easily be broken down into smaller stories
An example of an epic:
Story: Manage Content
As a admin user
I want to manage my sites pages
So that I can keep my website up-to-date
Scenario: Add a page
Scenario: Delete a page
Scenario: Edit a page
I find a good guideline is that a story should be accomplishable in around 1 week.
There are two ways you can split a epic story into snappy short stories:
Split Along create, add, edit and delete. or Disaggregate along the boundaries of the data
So initially I went with disaggregating along the data boundaries but still found the stories where looking big, so I then tried splitting on actions which left me with some nice snappy stories.
Story: Add page
As a admin user
I want to add pages
So that I can keep my website up-to-date
Scenario: ...
Story: Delete page
As a admin user
I want to delete pages
So that I can remove bad or old articles
Scenario: ...
Story: Edit page
As a admin user
I want to edit pages
So that I can refine my articles as time passes
Scenario: ...
Note how our understanding of the users goals has expanded here. We know in more detail what the user wants from the system.
When choosing which way to split stories I often write out what the stories and scenarios would look like which helps me get a feeling for which splitting method is best suited.
Stories should concentrate on outputs not inputs
Story: Creating an account
As a new user to the site
I want to create a account for the site
So that I can login and comment on articles
Scenario: valid account details
Given I'm logged out
And I've entered my username 'spoink'
And I've entered my website 'http://www.joesniff.co.uk'
And I've entered my email address 'spoink@joesniff.co.uk'
And I've entered my password 'poodle'
And I've entered my password 'poodle'
And I've entered a password reminder 'not a monkey'
And I've selected 'do not send me emails'
And I've selected 'I agree to terms and conditions'
When I click the create account button
Then my account will be created
And I will be redirected to my account page
Outputs represent value to the customer. They tend to be far more interested in what they get out of the system than what they put in.
Story: Creating an account
As a new user to the site
I want to create a account for the site
So that I can login and comment on articles
Scenario: valid account details
Given I'm logged out
When I create a new account
Then I will be redirect to my account page
And I will be presented with the last time I logged in
And my current community ranking
And the last comment I made.
In this example focusing on all the details of creating an account while useful missed the fact that the most important thing to the client was why the user was creating the account and what benefit/output they got that kept them there.
Stories should slice through multiple layers
Stories act as integration tests cutting through all the layers of your web application stack.
If you only cut through certain layers you introduce the possibility that the stories pass without parts of your system working!
Story: Fill in form
As a logged in user
I want to fill in a register form
So that I can register
Story: Save in database
As a logged in user
I want my form information saved in the database
So that the system remembers me
Yuck! Hearing the user talk about ‘the database’ is a sure sign these stories have problems.
Stories should be kept organised
I tend to use one of two ways of organising my stories in the file system:
1. Organise by Stakeholder
Contain the stories based on which stakeholder is acting in the story.
stories/
customer/
purchase.story
purchase.rb
web_master/
...
advertiser/
...
2. Organise by Feature
Contain the stories based on which feature stories relate to. (This is particularly useful if you’re structure follows Epic –> Feature –> Story ).
stories/
page_editing/
edit_page.story
edit_page.rb
image_uploading/
...
login
...
Use GivenScenario to make Stories easier to read and discuss
GivenScenario allows you to reuse scenarios which can help make the stories clean and in turn easier to discuss with the customer.
Story: maintaining blog
As a web admin
I want to manage pages within an admin system
So I can keep my blog up to date
Scenario: Add page
...
Scenario: edit page with valid details
GivenScenario: Add page
When I edit pages details
Then it should save without errors
Stories should not leave hidden magic in steps
When scenarios start to fail in your stories its often up to the developer to have a look why. You often turn to the story to check the steps.
...
Given there is a page 'testing'
...
Given(/there is a page '(.+)'/) do |title|
@page = Page.create!({ :title => title)
@page.images = Image.create!({ :test )
end
Misleading or not revealing hidden steps can making debugging stories a painful process. I changed the story to make sure it was obvious what was going on:
...
Given there is a page 'testing' with an image 'test'
...
Make your Given/When steps fail loudly
Stories can be hard to debug. Anything in the Given & When could have failed but we are only aware of a Then failing. So we have to search back through the Givens and Whens to spot an error.
The following could fail but the step would pass.
Given('there is a page') do
Page.create {:title => 'explode early'}
end
By throwing an exception we can ensure that if anything goes wrong the step fails
Given('there is a page') do
Page.create! {:title => 'explode early'}
end
Avoid relying heavily on instance values in steps
Relying on using instance values makes it difficult to reuse your steps. Also Interdependencies between steps can make them harder to debug.
Given('there is a page') do
@page = Page.create! {:title => 'dont do it!'}
end