Story Driven Development With Rails

September 12th, 2008
by Rein Henrichs

As a follow up on writing incremental stories,
we’re going to take the first story and walk through a behavior driven
development process to implement it in a simple Rails application.

We will focus on making small, iterative changes and following a strict
test-first philosophy where we write granular unit tests and implement them
with just enough code to make them pass.

Let’s review our first story:

Story #1

As A User I Want To View A List Of Projects
So that I can find a project that interests me
Acceptance:
* All projects shown in list
* List is paginated
* List is sorted by age
Cost: 1 Point

At this point, let’s assume that this story is on top of our current iteration.
First, we’ll need to review the story for any missing information and
communicate with the client to clear up any questions. Keep in mind that a
story is just a way to capture a conversation about a feature. It is not set in
stone. After talking to the client, we find that we will need to display a
project’s author name and title and that the title will need to link to that
project’s page. Let’s update the story appropriately.

Story #1

As A User I Want To View A List Of Projects
So that I can find a project that interests me
Acceptance:
* All projects shown in list
* Show title for each project
* Show author name for each project
* Project title links to the project's page
* List is paginated
* List is sorted by project age
Cost: 1 Point

Now that the story is complete, deliverable and acceptable, we can begin work
on the new feature. A implementation plan should be forming in your head. Now
is the time to divide the work into testable units. In our case we already have
a Project model with the requisite fields (let’s say) so our work will focus on
the controller and view.

Test All The F—ing Time

Client sign-off on well written acceptance tests means that the specifications
you write and the feature that is implemented as a result will be more closely
in line with the client’s expectations. This minimizes the kind of impedance
mismatch between expectation and execution that so often plagues a project with
poor client communication and a disorganized process.

Now it’s time to take our acceptance tests and use them to drive our iterative,
test-driven development process. Let’s take it from the top.

All Projects Shown in List

Let’s start with the controller. A list of projects needs an index action.
Starting at the top, we will need to load all of the projects. Let’s write a
test for this:

spec/controllers/projects_controller_spec.rb

describeProjectsControllerdodescribe"getting a list of products"doit"loads all the projects"doprojects=[mock_model(Project)]Project.stub!(:all).and_return(projects)get'index'assigns(:projects).should==projectsendendend

Note: Stubbing the call to Project.all has the immediate benefit of
eliminating the database from our test but is potentially more brittle since we
cannot be sure that this interface point to our Project model will not need to
change in the future.

On a side note, I tend to view controller tests as integration-level tests
rather than unit tests. As such, I usually do write tests that touch the
database since these are often less brittle. If you write tests that touch the
database, ActiveRecord factories such as Factory Girl
or object daddy are useful for
populating the database with valid records in known states.

Then we have to display a list of projects. We’ll write a view test to cover this:

spec/views/projects/index.html.erb_spec.rb

describe"/projects/index"dobefore(:each)do@project=mock_model(Project)@projects=[@project]assigns[:projects]=@projectsrender'projects/index'endit"should include a list of projects"doresponse.shouldhave_tag('li.project',:count=>@projects.size)endend

Note: View tests can often be brittle. They can be made less brittle by
testing only for semantically appropriate tags, classes and ids whenever
possible. Using semantically rich markup in your views will make it much easier
to write robust view tests – and is also a great practice for its own sake.
{:.note}

Show Title for Each Project

spec/views/projects/index.html.erb_spec.rb

describe"/projects/index"do# SNIPit"should show the title for each project"doresponse.shouldhave_tag('li.project .title',@project.title)endend

Note: We are using an accessor on our project model, Project#author_name.
There’s a good chance that this name will be taken from an associated User or
Author model in any non-trivial Rails application. From an object oriented
standpoint, however, having the author name hang directly from the Project
model improves encapsulation.

The benefits of this were already seen in the test, where we were able to stub
author_name directly on the Project mock. Without the accessor, we would be
forced to stub #author on the Project mock to return an Author mock that then
stubs #name just so that we could properly test the method chain
project.author.name that is used in the view. Violating the Law of Demeter
makes testing harder.

Project Title Links to the Project’s Page

spec/views/projects/index.html.erb_spec.rb

describe"/projects/index"do# SNIPit"should have project titles that link to the project page"doresponse.shouldhave_tag('li.project .title a[href=?]',project_path(@project),:text=>@project.title)endend

The story is now about half complete. I’ll leave pagination and default sort
order as an exercise for the user. In fact, these could also have been broken
out into a secondary story or stories given that what we have done so far is an
incremental unit of work.

I hope this rather contrived example shows how stories with well written
acceptance tests inform a test- or behavior- driven development process and
help bridge the gap between what the client expects and what the developement
team delivers.