O. Frabjous-Dey
2008-Oct-13 02:33 UTC
[rspec-users] 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. -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20081012/8d3b230e/attachment.html>
Nick Hoffman
2008-Oct-13 03:33 UTC
[rspec-users] Checking that a controller created a separate model object
On 2008-10-12, at 22:33, O. Frabjous-Dey wrote:> 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.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
Nick Hoffman
2008-Oct-13 20:47 UTC
[rspec-users] Checking that a controller created a separate model object
On 2008-10-12, at 23:49, O. Frabjous-Dey wrote:> Hi Nick, > > The :new action comes straight from script/generate rspec_scaffold: > > def new > @group = Group.new > > respond_to do |format| > format.html > end > end > > I took out the XML rendering, but left in the respond_to block just > in case I wanted to add it or something else later. > And here''s :create. > > def create > @group = Group.new(params[:group]) > > respond_to do |format| > if @group.save > flash[:notice] = ''Group was successfully created.'' > # Make this user an officer of the group > Membership.create(:user_id => session[:user_id], :group_id > => @group.id, :rank => ''officer'') > format.html { redirect_to(@group) } > else > format.html { render :action => "new" } > end > end > end > > 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.
O. Frabjous-Dey
2008-Oct-13 21:14 UTC
[rspec-users] Checking that a controller created a separate model object
On Mon, Oct 13, 2008 at 1:47 PM, Nick Hoffman <nick at deadorange.com> wrote:> On 2008-10-12, at 23:49, O. Frabjous-Dey wrote: > >> Hi Nick, >> >> The :new action comes straight from script/generate rspec_scaffold: >> >> def new >> @group = Group.new >> >> respond_to do |format| >> format.html >> end >> end >> >> I took out the XML rendering, but left in the respond_to block just in >> case I wanted to add it or something else later. >> And here''s :create. >> >> def create >> @group = Group.new(params[:group]) >> >> respond_to do |format| >> if @group.save >> flash[:notice] = ''Group was successfully created.'' >> # Make this user an officer of the group >> Membership.create(:user_id => session[:user_id], :group_id => @ >> group.id, :rank => ''officer'') >> format.html { redirect_to(@group) } >> else >> format.html { render :action => "new" } >> end >> end >> end >> >> 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. >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. -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20081013/222c5c3e/attachment-0001.html>
Nick Hoffman
2008-Oct-14 02:03 UTC
[rspec-users] Checking that a controller created a separate model object
On 2008-10-13, at 17:14, O. Frabjous-Dey wrote:> 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.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