johnreilly

www.flickr.com
john.reilly's items Go to john.reilly's photostream

Posts tagged "testing"

rspec is mocking me

Grr. I hate situations where it works here, but doesn’t work there, and I can’t see where the difference is. Something has to be different, I just can’t for the life of me figure out what. Maybe someone can help.

Here’s what’s up.

I’ve got rspec and rspec-rails installed as git submodules. Running the latest edge rails. I’m gonna generate up a quick rspec_scaffold:

$ script/generate rspec_scaffold Bike name:string sku:string

This generates the following controller test (abridged):


describe BikesController do

  def mock_bike(stubs={})
    @mock_bike ||= mock_model(Bike, stubs)
  end
  
  describe "responding to GET index" do

    it "should expose all bikes as @bikes" do
      Bike.should_receive(:find).with(:all).and_return([mock_bike])
      get :index
      assigns[:bikes].should == [mock_bike]
    end

  end
end

Looks fine. BUT. In my project, this spec fails, because it is somehow loading up the view and the name and sku calls are tripping up the mock, as it isn’t expecting them to be called in this controller test.

Note, however, that I am NOT calling integrate_views inside my controller spec, so I don’t have any idea why the view code is being run.

% rake spec:controllers
(snip)
ActionView::TemplateError in 'BikesController responding to GET index should expose all bikes as @bikes'
Mock 'Bike_1017' received unexpected message :name with (no args)
On line #11 of app/views/index.html.erb

8: 
9: <% for bike in @bikes %>
10:   <tr>
11:     <td><%=h bike.name %></td>
12:     <td><%=h bike.model %></td>
13:     <td><%= link_to 'Show', bike %></td>
14:     <td><%= link_to 'Edit', edit_bike_path(bike) %></td>

app/views/bikes/index.html.erb:11
app/views/bikes/index.html.erb:9:in `each'
app/views/bikes/index.html.erb:9

This happens for 10 of the 18 scaffolded specs.

I can fix the errors by adding expectations to the mock like so:


it "should expose all bikes as @bikes" do
  Bike.should_receive(:find).with(:all).and_return([mock_bike])
  mock_bike.should_receive(:name)      
  mock_bike.should_receive(:sku)
     
  get :index
  assigns[:bikes].should == [mock_bike]
end

But that kind of defeats the purpose of having a controller-only test, as it’s now testing the view code as well.

What’s weird is, I started a brand new project, and all the tests passed with flying colors!

% rails rspectest
% cd rspectest 
% rake db:create:all
% script/plugin install git://github.com/dchelimsky/rspec.git
% script/plugin install git://github.com/dchelimsky/rspec-rails.git
% script/generate rspec
% script/generate rspec_scaffold Bike name:string sku:string  
% rake db:migrate
% rake spec
(in /Users/john/src/test/rspectest)
.....................................

Finished in 1.006109 seconds

37 examples, 0 failures

So a brand new project runs the rspec_scaffold specs in isolation, but my project somehow doesn’t.

I’m tearing my hair out on this one. I can’t figure out what I’ve done to my project to cause these specs to stop working.

Anyone out there have any ideas/suggestions?

Update!

Wow. Through the magic of git bisect, I’ve determined that the problem occurs because of a change in edge rails. Turns out, this commit causes the specs to fail. Everything before it passes, everything after fails.

I have no idea how to fix this (or if it is rails’ or rspec’s fault), but I’m getting closer. :-)

PS, if you’ve never used git bisect before, you really haven’t lived.

Update 2!

The latest commits to rspec and rspec-rails fix the problem. Bravo rspec team.

Comments (View)
OH: the trick is to write your unit tests while you’re sober. then write the code while you’re drunk technoweenie
tagged as: quotes code testing
Comments (View)