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
    ...
  end

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

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
too.

------------------

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

Pat

On Tue, Oct 20, 2009 at 10:40 PM, Christoph ---- <li...@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@rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users
>
_______________________________________________
rspec-users mailing list
rspec-users@rubyforge.org
http://rubyforge.org/mailman/listinfo/rspec-users

Reply via email to