In a former life I used to write “functional specifications”. These were long, dense, hard-to-read documents that detailed what an application (not yet written) was supposed to do. I would spend (literally) weeks typing these things up, the customer would read it, think they understand and I would quote them based upon the document. And then the project would go over budget as all the tiny subtle details became apparent about two weeks before deadline day. But, even worse, the document would slowly become out of date, as changes were made in response to feedback – while the spec stayed unchanged.
As you might expect, this lead to general disillusionment with functional specifications. What’s the point if they didn’t help with the budget and didn’t reflect the actual application?
But when I read about Test-Driven Development the key thing that struck me was the tests became a living embodiment of what the application is supposed to do. They are a specification; but not only that, they are a specification that has to remain up to date.
Only one problem. They are written in code. Making them meaningless to the client. In fact, often, some tests were so obscure they were only meaningful to me; as I was writing them. Come back to it six months later and try and figure out why that change has made it fail? No idea.
Which is why RSpec and its Stories are so exciting. A Story is a text document that describes the required behaviour of the application. Read that again. A text document; written in English. So your clients can read them. Can help to write them. You then supply some Ruby code, that matches the sentences in your stories and associates them with code. That code is run, testing your application and proves that it does what it is supposed to (providing you’ve written your test code correctly of course).
Story: measure progress towards registration goals
As a conference organizer
I want to see a report of registrations
So that I can measure progress towards registration goals
Scenario: one registration shows as 1%
Given a goal of 200 registrations
When 1 attendee registers
Then the goal should be 1% achieved
Scenario: one registration less than the goal shows as 99%
Given a goal of 200 registrations
When 199 attendees register
Then the goal should be 99% achieved
Now, I have to admit, I’ve not used RSpec Stories in anger yet. But it has had a strong effect on my “unit” tests.
The difference between stories and unit tests are that stories test your full stack. Go to ‘/’, fill out the text fields, click the submit button, it should insert into the database successfully and then show these three records on the ‘/whatever’ page. Unit tests will check that the form is shown when you go to ‘/’. A separate test will check that your record can be inserted into the database. Another test will prove that ‘/whatever’ asks the database for three records.
But, having read about Stories, the way I write my unit tests have changed. Check this controller specification out:
describe ReportsController, "at the admin site" do
it "should show this month by default" do
given_the_admin_site
given_a_system_user
when_getting :index do
expect_to_find_orders_for Date.today
end
assigns[:date].should be_today
assigns[:orders].should == @orders
end
end
Not quite plain English. But, when it comes to maintenance, using given, expect and when as prefixes for your helpers makes a world of difference.
UPDATE: now using block syntax around the “when_getting” statement
[...] Two (related) thoughts on “The Specification is the Documentation“. [...]