views:

40

answers:

1

Hi there,

I've coded the following spec:

  it "should call user.invite_friend" do

    user = mock_model(User, :id => 1)
    other_user = mock_model(User, :id => 2)
    User.stub!(:find).with(user.id).and_return(user)
    User.stub!(:find).with(other_user.id).and_return(other_user)
    user.should_receive(:invite_friend).with(other_user)

    post :invite, { :id => other_user.id }, {:user_id => user.id}

  end

But I'm getting the following error when I run the specs

NoMethodError in 'UsersController POST invite should call user.invite_friend'
undefined method `find' for # Class:0x86d6918 
app/controllers/users_controller.rb:144:in `invite'
./spec/controllers/users_controller_spec.rb:13:

What's the mistake? Without .with it works just fine, but I want different return values for different arguments to the stub method. The following controller's actions might be relevant:

  def invite
    me.invite_friend(User.find params[:id])
    respond_to do |format|
      format.html { redirect_to user_path(params[:id]) }
    end
  end
  def me
    User.find(session[:user_id])
  end
+1  A: 

The error arises because the stub gets "used up" by the first call to find. The second find is not stubbed, so you get the NoMethodError.

Someone can correct me if I'm wrong, but .with appears to have an odd effect on stubs when you call it more than once. Rspec seems to associate each message of the same argument type with a single stub. But another argument type effectively creates a different stub. So in your case, you can fix it by calling the second find with a string:

User.stub!(:find).with(other_user.id.to_s).and_return(other_user)

which is lucky for you, because in fact the controller is expecting a string in the params hash.

This doesn't answer the larger question of: how do I stub multiple method calls with parameters of the same argument type? In my experience, you can't do that.

Of course you can get around it by not specifying the arguments at all. In your case, I'd say that testing find itself is not pertinent to your controller -- in effect, you're testing whether ActiveRecord can do a database lookup on a primary key, which is already well tested. So if all you need is to stub the responses to find in the right order, you can do this:

User.stub!(:find).and_return(user,other_user)

zetetic