rspec on rails, LocalJumpError - no block given

Posted by matt on September 24, 2007

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