Portrait of Edd Dumbill, taken by Giles Turnbull

Subscribe to updates

Feed icon Atom or RSS

or get email updates

What I make

expectnation
a conference management web application


XTech Conference
a European web technology conference

Telling stories with RSpec

For a while now I've been writing RSpec specifications to test Expectnation, rather than "traditional" Rails unit tests. Although I don't completely get the Behaviour Driven Development hype around RSpec, I do find that it helps in writing more literate and maintainable test suites.

Until very recently, however, RSpec for Rails had no decent answer to Rails' integration tests. Integration tests enable the testing of compete scenarios, as opposed to the specific testing of methods or single HTTP operations that unit and functional testing provide.

For instance, our integration test suite for Expectnation tests things such as "a user tries to submit a proposal, is redirected to the account sign up page, signs up, and then is redirected to the proposal submission page". This involves multiple steps, and the whole suite of tests necessitates a lot of reuse.

The need for reuse resulted in trying to construct a DSL-like set of methods for each step, such as "user logs in with email X and password Y". It has gotten a bit tangled, especially when looking at test failures to see what's really gone wrong.

Tangled wires

After a while, unravelling the test suite starts to feel like unpacking Christmas lights. (Photo: shoothead)

RSpec Story Framework

My problems with integration tests made the Story Framework, newly available in RSpec 1.1, look very interesting. Based around plain text descriptions of application behaviour, it lets you write integration tests with good reuse and good diagnostic reporting.

For example, here's a story I wrote to check the login process.

Story: login as an existing user
        As an unauthenticated user
        I want to log in to Expectnation
        So I can see my account details
       
        Scenario: login details are correct
                Given an event provider
                And my test@example.org account
                When I log in with email test@example.org and password foofoo
                Then I will be logged in
                And I will be shown the account page

The words such as "Given", "When" and "Then" are cues to the story runner to execute some code. Behind the story sits a collection of steps. Here's a couple of steps from this test:

  Given "my $email account" do |email|
    @user = find_or_create_user_by_email({:email => email,
      :password => 'foofoo',
      :password_confirmation => 'foofoo'})
  end

  When "I log in with email $email and
                         password $password" do |email, password|
    post '/user/account/authenticate',
      :user => {:email => email, :password => password}
  end

Notice how a clever bit of string matching allows you to pass parameters from the story prose.

With a small bit of bolting together, the prose stories are then run as code and the tests executed. One nice touch is that if you write a story which doesn't yet have code behind it, it is marked not as a pass or fail, but as pending, which means you can sit down and write all your stories and then get to coding the steps later. This beats writing empty tests which transparently pass.

To conclude, the RSpec Story Framework offers these advantages over Rails' integration tests:

  • Text-only stories makes it very easy to write and understand tests
  • The construction of steps for each line makes it easy and maintainable to construct reusable components for the tests

Further reading

blog comments powered by Disqus


You are reading the weblog of Edd Dumbill, writer, programmer, entrepreneur and free software advocate.
Copyright © 2000-2012 Edd Dumbill