[rspec-users] how to mock active record relationships

Ashley Moran ashley.moran at patchspace.co.uk
Wed Oct 21 03:39:08 EDT 2009


On 21 Oct 2009, at 06:40, Christoph ---- wrote:
> Hi
> I am new to Rspec and try to mock my controller that looks like
>
> def create
>    @friendship = current_user.friendships.build(:friend_id =>
> params[:friend_id])
>    if @friendship.save
>      flash[:notice] = "Added friend."
>      render :text => flash[:notice]
>    else
>      flash[:error] = "Error occurred when adding friend."
>      render :text => flash[:notice]
>    end
>  end
>
> how can I mock
> current_user.friendships.build ?
>
> I tried
>
> before(:each) do
>    @friend_ship = mock_model(Friendship, :friend_id => 1947284801,
> :user_id => 134245544)
>    controller.stub!(:current_user).should_receive 
> ('friendships').and_return(@friend_ship)
> end
>
> but get
> You have a nil object when you didn't expect it! The error occurred
> while evaluating nil.friendship

Hi Christoph ----

You're making three mistakes:
(1) confusing the collection with the model (friendships vs friendship)
(2) using the wrong stub syntax (I think - I don't actually chain  
calls after `stub` together like that)
(3) putting an expectation in the `before` block

If you *really* want to do it this way, you'd have to write something  
along these lines:

   describe User do
     before(:each) do
       @friendship = mock_model(
         Friendship, :friend_id => 1947284801, :user_id => 134245544
       )
       # Issue (1)
       @friendships = mock("ActiveRecord collection", :build =>  
@friendship)

       # Issue (1)
       @user = mock_model(User, :friendships => @friendships)

       # Issue (2)
       controller.stub!(:current_user).and_return(@user)
     end

     it "should build a user" do
       # Issue (3)
       @friendships.should_receive(:build).with(:friend_id =>  
YOUR_EXPECTED_FRIEND_ID)
       # Put whatever rspec-rails provides to invoke the action here
     end
   end

(disclaimer - not run)

However - this is a fragile solution because it makes assumptions  
about the ActiveRecord magic in User (it's a violation of Law of  
Demeter[1], which I believe you have to pay extra attention to with  
ActiveRecord code).  It is more stable to write a method  
User#build_friendship that takes the appropriate args - you can then  
spec the behaviour in the model layer.

Sorry I can't go into more detail about the above issues - I'm writing  
this over a quick coffee in the station before I go to the office.

HTH

Ashley

[1] http://en.wikipedia.org/wiki/Law_of_Demeter

-- 
http://www.patchspace.co.uk/
http://www.linkedin.com/in/ashleymoran
http://aviewfromafar.net/









More information about the rspec-users mailing list