Mocking with RSpec

It's been a while since I heaped any praise on RSpec, but I just had a nice experience with mock objects provided by the framework and wanted to briefly mention it here.

First up, the problem; the following buggy code in the admin interface for moderating issues:

def index
  @paginator  = Paginator.new params,
                              Issue.count(:conditions => { :awaiting_moderation => true }),
                              admin_issues_url
  @issues     = Issue.find :all,
                           :offset => @paginator.offset,
                           :conditions => { :awaiting_moderation => true },
                           :order => 'created_at DESC'
end

The Paginator instance by default assumes 10 records per page, but the corresponding find statement on the Issue model has no limit at all, always returning all the records awaiting moderation.

So the fix will be to add a :limit clause to the find statement. While we're at it we'll raise the per-page record count from its default of 10 up to 20:

def index
  @paginator  = Paginator.new params,
                              Issue.count(:conditions => { :awaiting_moderation => true }),
                              admin_issues_url,
                              20
  @issues     = Issue.find :all,
                           :offset => @paginator.offset,
                           :conditions => { :awaiting_moderation => true },
                           :order => 'created_at DESC',
                           :limit => @paginator.limit
end

Before committing this fix, however, I want to add a regression test to my spec suite to make sure that the bug never creeps back in.

RSpec's message expectations allow me to define pretty much exactly what I want and no more:

it 'should paginate in groups of 20' do
  paginator = Paginator.new({}, 100, 'foo', 20)
  Paginator.should_receive(:new).with(anything(), anything(), anything(), 20).and_return(paginator)
  Issue.should_receive(:find).with(anything(), hash_including(:limit => paginator.limit))
  do_get
end

Basically I want to make sure that the Paginator receives a per-page limit of 20, and also that Issue gets the corresponding :limit clause. The anything() and hash_including() expectation helpers provide me with pretty much exactly what I want, allowing me to specify only the bits that are actually relevant to the expectation and leaving the rest unspecified.

Good stuff!

← February server maintenance
Firefox and self-signed SSL certificates →

All blog posts