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
Trackbacks

Use this link to trackback from your own site.

Comments

Leave a response

  1. […] unknown wrote an interesting post today!.Here’s a quick excerptIn 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. … […]

  2. Clemens Wed, 02 Apr 2008 14:38:50 BST

    Thanks - had exactly the same problem. Your post cleared that up.

  3. matt Wed, 02 Apr 2008 14:49:22 BST

    Glad I could help Clemens. Just took a look at http://tupalo.com/ and I love it! Looks like a much nicer and more modern version of a site I used to work for called upmystreet.com.

  4. Ivor Tue, 20 May 2008 19:27:54 BST

    Thanks!

  5. Britt Sun, 29 Jun 2008 01:14:15 BST

    Hi Matt,

    Have you found any better way to organize this kind of speccing?
    I am going through the same pains as you and have begun to go down the road of nested describe sequences… but it still requires me to mock EVERYTHING at the first before(:each) declaration. I would like to incrementally mock/stub as i begin working down through my code and nested describe blocks

  6. matt Sun, 29 Jun 2008 09:06:13 BST

    Hi Brit,

    Since the introduction of foxy fixtures in Rails 2.0 I have actually reverted to using fixtures. I have had quite a few problems using mocks/stubs because imho they result in brittle tests. Here’s an example using TestUnit http://szeryf.wordpress.com/2008/01/07/does-stubbing-make-your-tests-brittle/ but the same applies to rspec. Generally, I will only use mocks now when I need to test my applications interaction with an external library, or something where the implementation is fixed. Maybe I’ll write a separate post on this sometime.

Comments