[rspec-users] how to mock active record relationships

Pat Maddox pat.maddox at gmail.com
Wed Oct 21 04:15:42 EDT 2009

user = mock('user')
controller.stub(:current_user).and_return user
friendships_proxy = mock('friendships proxy')
user.stub(:friendships).and_return friendships_proxy
friendship = mock('friendship')
friendhips_proxy.stub(:build).and_return friendship

Try to avoid chains like this.  Makes the test setup noisy, and the
test brittle.  One thing I like to do is wrap up the association chain
in a method and stub that.  So it would look something like:

class FriendshipsController < ApplicationController
  def create
    @friendship = build_friendship

  def build_friendship
    current_user.friendships.build(:friend_id => params[:friend_id])

then the mock setup becomes:

friendship = mock('friendship')
controller.stub(:build_friendship).and_return friendship

Typically you do not want to mock the object under test but my
experience has been that this is one situation where it pays to break
the rules.  AR encourages a style that is not particularly friendly to
unit testing.  With this pattern you can have your cake and eat it


A couple other notes:

1. I'm not a fan of object.collection.build.  If save fails, then the
built object is still in the in-memory collection.  If you iterate
through that anywhere on the page, you'll use/render an invalid
record.  I see people use object.collection.build all the time and
frankly don't understand why it doesn't bite them more frequently.  I
guess they just don't iterate over the collection on the "new object"
page that often

2. You might consider creating a Friendship object directly, something
like  Friendship.create(:members => [current_user,
User.find(params[:friend_id]).  Really easy to test because you don't
need to stub a huge association chain, you can just expect one call to
Friendship.create.  From a design perspective, neither user object has
more importance than the other one, so why is the current user given
priority in the code?  It's not a strong argument in this case, but
the same sorta thing crops up all the time.  A classic example is a
transfer in between bank accounts - does a bank account know how to
transfer money to another account, or is there a bank or banker object
that knows how to do a transfer between accounts?  Or maybe you create
a transfer transaction and post it to one or more ledgers.  As with
anything, there are a number of potential ways you could model it.
One thing I've noticed is that AR gives you the ability to write
highly readable code, but in doing so people tend to overlook the
subtleties of their domain model.

3. Using a framework like resource_controller will drastically reduce
the number of controller specs you need to write


On Tue, Oct 20, 2009 at 10:40 PM, Christoph ---- <lists at ruby-forum.com> 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
> --
> Posted via http://www.ruby-forum.com/.
> _______________________________________________
> rspec-users mailing list
> rspec-users at rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users

More information about the rspec-users mailing list