rspec on rails, LocalJumpError - no block given
Quite frequently in rails I find myself saying something like:
def show
@photo = User.find(params[:user_id]).photos.find(params[:id])
end
Pretty simple right…
But it can be the cause of headaches when trying to write rspec tests. Writing the following test:
describe PhotosController, ‘handling GET /users/1/photos/2.png’ do
before(:each) do
@photo = mock_model(Photo)
@user = mock_model(User, :photos => [@photo])
[@photo].stub!(:find).and_return(@photo)
User.stub!(:find).and_return(@user)
end
def do_get
get ’show’, :user_id => 1, :id => 2, :format => ‘png’
end
it “should find the specified user” do
User.should_receive(:find).once.with(‘1′).and_return(@user)
do_get
end
it “should find the found users photos” do
@user.should_receive(:properties).once.and_return([@photo])
do_get
end
it “should find the specified photo from within the found photos” do
[@photo].should_receive(:find).once.with(‘2′).and_return(@photo)
do_get
end
end
Will result in the following errors:
LocalJumpError in 'PhotosControllerhandling GET /users/1/photos/2.png should find the specified user'
no block given
LocalJumpError in 'PhotosControllerhandling GET /users/1/photos/2.png should find the found users photos'
no block given
LocalJumpError in 'PhotosControllerhandling GET /users/1/photos/2.png should find the specified photo from within the found photos'
no block given
The cause of the problem is this. When rspec runs it is calling the Ruby Enumerable#find on an array of one object [@photo]. Enumerable#find expects a block. In the rails code, ActiveRecord::Base::find is being called which is really doing a scoped find for all photos belonging to the user. The solution is to stub the find method on the photos array in the before block of your rspec tests. Here is the working test:
describe PhotosController, ‘handling GET /users/1/photos/2.png’ do
before(:each) do
@photo = mock_model(Photo)
@photos = [@photo]
@photos.stub!(:find).and_return(@photo)
@user = mock_model(User, :photos => @photos)
User.stub!(:find).and_return(@user)
end
def do_get
get ’show’, :user_id => 1, :id => 2, :format => ‘png’
end
it “should find the specified user” do
User.should_receive(:find).once.with(‘1′).and_return(@user)
do_get
end
it “should find the found users photos” do
@user.should_receive(:properties).once.and_return(@photos)
do_get
end
it “should find the specified photo from within the found photos” do
@photos.should_receive(:find).once.with(‘2′).and_return(@photo)
do_get
end
end