Checking that a controller created a separate model object

Hi everyone, RSpec newbie here. I’m looking forward to interacting with
the
community as I learn more about TDD, RSpec, Rails, and… TDD through
RSpec
in Rails.

Having watched the Peepcode screencasts and read a lot of documentation,
I’m
trying to write my first comprehensive applications using TDD from the
ground up. I’m stuck writing a test for my controller, though.

This application is your basic crappy social network. There are three
relevant models in play right now: User, Group, and Membership. User
and
Group have :has_many relationships with each other :through Membership;
Membership, in addition to having a user_id and a group_id, also has a
column called “rank”, to which I write in either “officer” or “member”
as a
value.

Group also defines the following relationships just for convenience:

has_many :memberships
has_many :officers, :through => :memberships, :source => :user,
:conditions => “rank = ‘officer’”
has_many :members, :through => :memberships, :source => :user,
:conditions
=> “rank = ‘member’”

I’m trying to test the CREATE functionality in my Group controller.
Unfortunately, this test is not working.

  it "should make the creating user an officer" do
    Group.stub!(:new).and_return(mock_group(:save => true))
    post :create, :group => {}
    assigns[:group].should have(1).officer
  end

The error message:
Mock ‘Group_1008’ received unexpected message :officer with (no args)

If I understand this correctly, I am getting the error because
assigns[:group] isn’t really an ActiveRecord object, just a mock, and I
guess the relationships don’t carry over? If this is the case, what’s
the
right way to test it, assuming that a Membership really is being created
in
my controller’s create function?

Thanks,
O.

On 2008-10-12, at 22:33, O. Frabjous-Dey wrote:

three relevant models in play right now: User, Group, and
has_many :members, :through => :memberships, :source

Thanks,
O.

Hi O. The Group that you’re creating is a Mock. Thus, it doesn’t have
any of the methods (Eg: #officer) that a real Group has. Since
#officer hasn’t been stubbed out on the mock Group, the error occurs.

As for how to spec that the relationships are being setup correctly,
can you provide the code for the Group’s “new” action?

Cheers,
Nick

On 2008-10-12, at 23:49, O. Frabjous-Dey wrote:

end
flash[:notice] = ‘Group was successfully created.’
Thanks!
O.

Hi again, O. In your spec, you’re stubbing Group#new and returning a
mock. As a result, the “create” action uses that mock when creating
the Membership object. I’ve never specced relationships, so I’m not
sure what to suggest. Hopefully someone else can give some advice.

Cheers,
Nick

BTW, that last email of yours was sent directly to me, rather than to
the mailing list. Let’s keep all of the messages on the list.

On Mon, Oct 13, 2008 at 1:47 PM, Nick H. [email protected]
wrote:

 format.html

respond_to do |format|
end
Cheers,
Nick

BTW, that last email of yours was sent directly to me, rather than to the
mailing list. Let’s keep all of the messages on the list.

Whoops! Sorry this was sent straight to you, Nick. I should have hit
Reply-all. (Also that the subject line isn’t formatted correctly; I
mistakenly thought that the listserv software would prepend
[rspec-users] on
its own.)

I thought some more about the issue and I think I’m approaching the
problem
the wrong way to begin with. As I understand it, part of the philosophy
of
RSpec is that using mocks and stubs when testing controllers and views
instead of touching the database helps to keep each test context
self-contained. So I really ought to be checking to see if
Membership.create or or Membership.new is being called instead of
examining
the model object’s relationships themselves - not that I can anyway,
since
it’s a mock model.

Can anyone confirm if that sounds right? If so, what method should I be
using? #should_receive? Thanks again in advance.

On 2008-10-13, at 17:14, O. Frabjous-Dey wrote:

I be using? #should_receive? Thanks again in advance.
Hi O. You’re correct about using mocks and stubs to prevent hitting
the database. More importantly though, they allow you to test discrete
pieces of code without depending on the state of its surrounding code.

To spec the ‘create’ action, just go through it one “step” at a time.
Eg:

describe “create” do
before :each do
@group = mock_model Group, :save => true
# somehow spec the assignment to flash[:notice]
Membership.stub! :create
end

it “should create a new group”
it “should respond to …”
it “should save the group”
it “should assign the flash message”
it “should create a new Membership”
etc…
end

That doesn’t seem complete. What’d I miss? =P

Cheers,
Nick