Thursday, December 11, 2008

I read an interesting post today by David Chelimsky who wrote RSpec A case against a case against mocking and stubbing. Its about mocking in testing and isolated vs integrated tests. I liked it all but what I particularly liked is how it describes the process of outside-in development.

To quote:


  1. Write scenarios in plain text with cucumber (driven by user stories, organized in features).

  2. Write the code for a step (or part of a step), run the feature, and observe the failure.

  3. Optionally (yes, it depends – and why is the topic for another blog) drive out a view with a view spec. When I say “drive out,” I mean a very granular Red/Green/Refactor cycle that only involves this view, and only enough of this view to support satisfaction of the failing step in the cucumber feature.

  4. Drive out a controller action using the same, granular Red/Green/Refactor cycle. And it may not be the entire controller action I think I want, covering all the cases I think I want. Just enough to support satisfaction of the failing step.

  5. Drive out the parts of the model that I need to satisfy the failing step, using the same granular R/G/R process.

  6. Run the cucumber feature and assess where I am.



In my projects I think I'm pretty good at following 3,4 &5 but I've struggled with steps 1 & 6. With the advent of cucumber I think now is the time to work on those other steps. I'll let you know how it goes in the New Year.

Saturday, December 6, 2008

Bug/patch with rspec-rails and helper instance variables

I finally got around to upgrading my version of rspec-rails from one that's almost a year old and came across an issue with the way implicit module inclusion is handled.

If you have a handler that uses memoization to cache some information in instance variables such as this (I'm not sure if this is a smell but my project has some examples of it)


module UsersHelper
def all_users
@users ||= User.find(:all)
end
end


You would expect this spec to work

describe UsersHelper do
it "should find all users" do
User.expects(:find).with(:all).returns(result=mock)
helper.all_users.should == result
end
end


Sometimes it does but if any other spec has called helper.all_users previously it will fail as the memoized @users variable is not nil so the collection is reused.

I have submitted a lighthouse patch to rspec-rails that fixes the problem so hopefully they will agree to fix it soon. Until then patch is available on github in my fork or rspec-rails if you want to use it.

Knows which specs are slowing down my spec suite

I often watch my specs run with the ........'s marching across my terminal and sometimes it seems to pause as it hits a particularly slow one. I figure there's some poorly written spec that's slowing my entire suite down and I should figure out what it is and fix it. But I rarely (if ever) take the time to track down the offender so never fix it. I just discovered that the clever folks writing rspec have taken away my excuse for laziness. I can tell it to report on the slowest specs by putting a line in my spec.opts file telling it to use the ProfileFormatter (see the first line below)


--format profile
--colour
--loadby mtime
--reverse


For example running the specs on my current project I get this output.


Profiling enabled.
........(a bunch more dots)

Top 10 slowest examples:
2.4908950 WikiContent should find all activity for a user
0.8061750 WikiContent should find recent activity for a user in last 15 days
0.2438460 WikiContent::Version should allow limit on results from finding recent activity
0.1821710 User should have a list of owned content
0.1700670 WikiContent should find results if access type is not same as role of the user but role of user is 'Leader'
0.1593900 ContentsController allows PUT request to 'update' action for 'member' role
0.1514150 Search::SearchResponse gets the wiki content id of an asset from the xml response
0.1470310 Content should always allow leaders to see all pages
0.1394920 Page should delete all comments when destroyed
0.1370330 ContentsController should save content and create the uploaded asset on successful POST to the 'create' action


Over the next few days I will find time to take a look at 'WikiContent find all activity' and 'WikiContent find recent activity' specs as they are the two clear outliers.

I now have no excuse to not optimize my slowest specs because I didn't know which they were!