Daniel Salmeron Amselem
2010-Jul-17 08:29 UTC
[rspec-users] assigns and should eql don''t work as expected
Today I''ve been writing some tests for a new rails 3 app, but after reading the doc from http://rdoc.info/projects/rspec/rspec-expectations, I still can''t understand why the test doesn''t work. My setup is: rvm 0.1.41 ruby 1.9.2dev (2010-07-11 revision 28618) [x86_64-darwin10.4.0] -> ruby 1.9.2-rc2 rspec 2.0.0.beta.17 rspec-rails 2.0.0.beta.17 devise 1.1.rc2 This is the test for the controller: require ''spec_helper'' describe PeopleController do describe "routes" do it "should route to GET people#new" do {:get => "/people/new"}.should route_to(:controller => "people", :action => "new") end end describe "Methods" do before :each do @member = Factory(:member) sign_in @member @person = @member.build_person end it "should render form for a new person on GET people#new" do @member.should_receive(:build_person).and_return(@person) get :new assigns[:person].should eql(@person) response.should be_success response.should render_template("new") end end end And the controller: class PeopleController < ApplicationController before_filter :authenticate_member! def new @person = current_member.build_person end end When running the test I get: .F................. 1) PeopleController Methods should render form for a new person on GET people#new Failure/Error: assigns[:person].should eql(@person) expected #<Person id: nil, first_name: nil, last_name: nil, gender: nil, university: nil, year: nil, email: nil, phone: nil, house: nil, user_account_id: 126, user_account_type: "Member", home_town: nil, bio: nil, current_location: nil, high_school: nil, undergrad: nil, profession: nil, concentration: nil, created_at: nil, updated_at: nil> got #<Person id: nil, first_name: nil, last_name: nil, gender: nil, university: nil, year: nil, email: nil, phone: nil, house: nil, user_account_id: 126, user_account_type: "Member", home_town: nil, bio: nil, current_location: nil, high_school: nil, undergrad: nil, profession: nil, concentration: nil, created_at: nil, updated_at: nil> (compared using eql?) # ./spec/controllers/people_controller_spec.rb:24:in `block (3 levels) in <top (required)>'' Finished in 2.29 seconds 19 examples, 1 failure The error with the full backtrace here: http://gist.github.com/479362 Which doesn''t seem to make sense. Any ideas? Thanks.
David Chelimsky
2010-Jul-17 13:09 UTC
[rspec-users] assigns and should eql don''t work as expected
On Jul 17, 2010, at 3:29 AM, Daniel Salmeron Amselem wrote:> Today I''ve been writing some tests for a new rails 3 app, but after > reading the doc from http://rdoc.info/projects/rspec/rspec-expectations, > I still can''t understand why the test doesn''t work. My setup is: > > rvm 0.1.41 > ruby 1.9.2dev (2010-07-11 revision 28618) [x86_64-darwin10.4.0] -> > ruby 1.9.2-rc2 > rspec 2.0.0.beta.17 > rspec-rails 2.0.0.beta.17 > devise 1.1.rc2 > > This is the test for the controller: > > > require ''spec_helper'' > > describe PeopleController do > > describe "routes" do > it "should route to GET people#new" do > {:get => "/people/new"}.should route_to(:controller => > "people", :action => "new") > end > end > > describe "Methods" do > > before :each do > @member = Factory(:member) > sign_in @member > @person = @member.build_person > end > > it "should render form for a new person on GET people#new" do > @member.should_receive(:build_person).and_return(@person) > > get :new > > assigns[:person].should eql(@person) > response.should be_success > response.should render_template("new") > end > end > > end > > And the controller: > > class PeopleController < ApplicationController > before_filter :authenticate_member! > > def new > @person = current_member.build_person > end > > end > > When running the test I get: > > > .F................. > > 1) PeopleController Methods should render form for a new person on GET > people#new > Failure/Error: assigns[:person].should eql(@person) > > expected #<Person id: nil, first_name: nil, last_name: nil, > gender: nil, university: nil, year: nil, email: nil, phone: nil, > house: nil, user_account_id: 126, user_account_type: "Member", > home_town: nil, bio: nil, current_location: nil, high_school: nil, > undergrad: nil, profession: nil, concentration: nil, created_at: nil, > updated_at: nil> > got #<Person id: nil, first_name: nil, last_name: nil, > gender: nil, university: nil, year: nil, email: nil, phone: nil, > house: nil, user_account_id: 126, user_account_type: "Member", > home_town: nil, bio: nil, current_location: nil, high_school: nil, > undergrad: nil, profession: nil, concentration: nil, created_at: nil, > updated_at: nil>Here''s how ActiveRecord defines == (to which it delegates from eql?) http://github.com/rails/rails/blob/c6e20586372743ce200449bf0ac21aed04c6b81e/activerecord/lib/active_record/base.rb#L1536 It returns false if the record has no id (!comparison_object.new_record?), even if all of the other attributes match. In order to get this to pass you have to actually save the object so it has an id, not just build it. I discussed this with Rails core members a year or two ago and while they agreed this would make testing easier, there were two motivating arguments not to change it: a) conceptually, id-less records are not necessarily the same entity (this one is a bit fuzzy to me) and b) it''s a risky change given the amount of rails code in existence. The other thing you can do is skip the stubbing and just say: assigns(:person).should be_a_new(Person) You could also write a custom matcher - something like match_new_record(other) that compares all of the attributes. Maybe it''s have_same_attributes_as: assigns(:person).should have_same_attributes_as(@person) I''d consider adding that to rspec-rails. Might be good to have a matcher with docs that explain all this to help avoid this sort of confusion in the future. WDYT?, David> > (compared using eql?) > # ./spec/controllers/people_controller_spec.rb:24:in `block (3 > levels) in <top (required)>'' > > > Finished in 2.29 seconds > 19 examples, 1 failure > > The error with the full backtrace here: http://gist.github.com/479362 > > Which doesn''t seem to make sense. Any ideas? > > Thanks. > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users
David Chelimsky
2010-Jul-17 13:41 UTC
[rspec-users] assigns and should eql don''t work as expected
On Jul 17, 2010, at 8:09 AM, David Chelimsky wrote:> > On Jul 17, 2010, at 3:29 AM, Daniel Salmeron Amselem wrote: > >> Today I''ve been writing some tests for a new rails 3 app, but after >> reading the doc from http://rdoc.info/projects/rspec/rspec-expectations, >> I still can''t understand why the test doesn''t work. My setup is: >> >> rvm 0.1.41 >> ruby 1.9.2dev (2010-07-11 revision 28618) [x86_64-darwin10.4.0] -> >> ruby 1.9.2-rc2 >> rspec 2.0.0.beta.17 >> rspec-rails 2.0.0.beta.17 >> devise 1.1.rc2 >> >> This is the test for the controller: >> >> >> require ''spec_helper'' >> >> describe PeopleController do >> >> describe "routes" do >> it "should route to GET people#new" do >> {:get => "/people/new"}.should route_to(:controller => >> "people", :action => "new") >> end >> end >> >> describe "Methods" do >> >> before :each do >> @member = Factory(:member) >> sign_in @member >> @person = @member.build_person >> end >> >> it "should render form for a new person on GET people#new" do >> @member.should_receive(:build_person).and_return(@person) >> >> get :new >> >> assigns[:person].should eql(@person) >> response.should be_success >> response.should render_template("new") >> end >> end >> >> end >> >> And the controller: >> >> class PeopleController < ApplicationController >> before_filter :authenticate_member! >> >> def new >> @person = current_member.build_person >> end >> >> end >> >> When running the test I get: >> >> >> .F................. >> >> 1) PeopleController Methods should render form for a new person on GET >> people#new >> Failure/Error: assigns[:person].should eql(@person) >> >> expected #<Person id: nil, first_name: nil, last_name: nil, >> gender: nil, university: nil, year: nil, email: nil, phone: nil, >> house: nil, user_account_id: 126, user_account_type: "Member", >> home_town: nil, bio: nil, current_location: nil, high_school: nil, >> undergrad: nil, profession: nil, concentration: nil, created_at: nil, >> updated_at: nil> >> got #<Person id: nil, first_name: nil, last_name: nil, >> gender: nil, university: nil, year: nil, email: nil, phone: nil, >> house: nil, user_account_id: 126, user_account_type: "Member", >> home_town: nil, bio: nil, current_location: nil, high_school: nil, >> undergrad: nil, profession: nil, concentration: nil, created_at: nil, >> updated_at: nil> > > Here''s how ActiveRecord defines == (to which it delegates from eql?) > > http://github.com/rails/rails/blob/c6e20586372743ce200449bf0ac21aed04c6b81e/activerecord/lib/active_record/base.rb#L1536 > > It returns false if the record has no id (!comparison_object.new_record?), even if all of the other attributes match. In order to get this to pass you have to actually save the object so it has an id, not just build it. > > I discussed this with Rails core members a year or two ago and while they agreed this would make testing easier, there were two motivating arguments not to change it: a) conceptually, id-less records are not necessarily the same entity (this one is a bit fuzzy to me) and b) it''s a risky change given the amount of rails code in existence. > > The other thing you can do is skip the stubbing and just say: > > assigns(:person).should be_a_new(Person) > > You could also write a custom matcher - something like match_new_record(other) that compares all of the attributes. Maybe it''s have_same_attributes_as: > > assigns(:person).should have_same_attributes_as(@person) > > I''d consider adding that to rspec-rails. Might be good to have a matcher with docs that explain all this to help avoid this sort of confusion in the future.Another option would be to add something like this: RSpec.configure do |c| c.treat_new_active_record_objects_as_equal end ... and have that override ==(other) on ActiveRecord::Base. It''s invasive, but you have to take an action to do the override. WDYT?> > WDYT?, > David > > >> >> (compared using eql?) >> # ./spec/controllers/people_controller_spec.rb:24:in `block (3 >> levels) in <top (required)>'' >> >> >> Finished in 2.29 seconds >> 19 examples, 1 failure >> >> The error with the full backtrace here: http://gist.github.com/479362 >> >> Which doesn''t seem to make sense. Any ideas? >> >> Thanks. >> _______________________________________________ >> rspec-users mailing list >> rspec-users at rubyforge.org >> http://rubyforge.org/mailman/listinfo/rspec-users >
Daniel Salmeron Amselem
2010-Jul-17 19:25 UTC
[rspec-users] assigns and should eql don''t work as expected
Thanks David, this was driving me crazy. In my opinion, I think will be a great idea to add a new matcher to rspec-rails to do this kind of comparison better than overriding == , and have total control of what you want to test in each case. Anyways, thanks for helping me on this. On Jul 17, 9:41?am, David Chelimsky <dchelim... at gmail.com> wrote:> On Jul 17, 2010, at 8:09 AM, David Chelimsky wrote: > > > > > > > > > On Jul 17, 2010, at 3:29 AM, Daniel Salmeron Amselem wrote: > > >> Today I''ve been writing some tests for a new rails 3 app, but after > >> reading the doc fromhttp://rdoc.info/projects/rspec/rspec-expectations, > >> I still can''t understand why the test doesn''t work. My setup is: > > >> rvm 0.1.41 > >> ruby 1.9.2dev (2010-07-11 revision 28618) [x86_64-darwin10.4.0] -> > >> ruby 1.9.2-rc2 > >> rspec 2.0.0.beta.17 > >> rspec-rails 2.0.0.beta.17 > >> devise 1.1.rc2 > > >> This is the test for the controller: > > >> require ''spec_helper'' > > >> describe PeopleController do > > >> describe "routes" do > >> ? it "should route to GET people#new" do > >> ? ? {:get => "/people/new"}.should route_to(:controller => > >> "people", :action => "new") > >> ? end > >> end > > >> describe "Methods" do > > >> ? before :each do > >> ? ? @member = Factory(:member) > >> ? ? sign_in @member > >> ? ? @person = @member.build_person > >> ? end > > >> ? it "should render form for a new person on GET people#new" do > >> ? ? @member.should_receive(:build_person).and_return(@person) > > >> ? ? get :new > > >> ? ? assigns[:person].should eql(@person) > >> ? ? response.should be_success > >> ? ? response.should render_template("new") > >> ? end > >> end > > >> end > > >> And the controller: > > >> class PeopleController < ApplicationController > >> before_filter :authenticate_member! > > >> def new > >> ? @person = current_member.build_person > >> end > > >> end > > >> When running the test I get: > > >> .F................. > > >> 1) PeopleController Methods should render form for a new person on GET > >> people#new > >> ? Failure/Error: assigns[:person].should eql(@person) > > >> ? expected #<Person id: nil, first_name: nil, last_name: nil, > >> gender: nil, university: nil, year: nil, email: nil, phone: nil, > >> house: nil, user_account_id: 126, user_account_type: "Member", > >> home_town: nil, bio: nil, current_location: nil, high_school: nil, > >> undergrad: nil, profession: nil, concentration: nil, created_at: nil, > >> updated_at: nil> > >> ? ? ? ?got #<Person id: nil, first_name: nil, last_name: nil, > >> gender: nil, university: nil, year: nil, email: nil, phone: nil, > >> house: nil, user_account_id: 126, user_account_type: "Member", > >> home_town: nil, bio: nil, current_location: nil, high_school: nil, > >> undergrad: nil, profession: nil, concentration: nil, created_at: nil, > >> updated_at: nil> > > > Here''s how ActiveRecord defines == (to which it delegates from eql?) > > >http://github.com/rails/rails/blob/c6e20586372743ce200449bf0ac21aed04... > > > It returns false if the record has no id (!comparison_object.new_record?), even if all of the other attributes match. In order to get this to pass you have to actually save the object so it has an id, not just build it. > > > I discussed this with Rails core members a year or two ago and while they agreed this would make testing easier, there were two motivating arguments not to change it: a) conceptually, id-less records are not necessarily the same entity (this one is a bit fuzzy to me) and b) it''s a risky change given the amount of rails code in existence. > > > The other thing you can do is skip the stubbing and just say: > > > ?assigns(:person).should be_a_new(Person) > > > You could also write a custom matcher - something like match_new_record(other) that compares all of the attributes. Maybe it''s have_same_attributes_as: > > > ?assigns(:person).should have_same_attributes_as(@person) > > > I''d consider adding that to rspec-rails. Might be good to have a matcher with docs that explain all this to help avoid this sort of confusion in the future. > > Another option would be to add something like this: > > RSpec.configure do |c| > ? c.treat_new_active_record_objects_as_equal > end > > ... and have that override ==(other) on ActiveRecord::Base. It''s invasive, but you have to take an action to do the override. > > WDYT? > > > > > > > > > WDYT?, > > David > > >> ? (compared using eql?) > >> ? # ./spec/controllers/people_controller_spec.rb:24:in `block (3 > >> levels) in <top (required)>'' > > >> Finished in 2.29 seconds > >> 19 examples, 1 failure > > >> The error with the full backtrace here:http://gist.github.com/479362 > > >> Which doesn''t seem to make sense. Any ideas? > > >> Thanks. > >> _______________________________________________ > >> rspec-users mailing list > >> rspec-us... at rubyforge.org > >>http://rubyforge.org/mailman/listinfo/rspec-users > > _______________________________________________ > rspec-users mailing list > rspec-us... at rubyforge.orghttp://rubyforge.org/mailman/listinfo/rspec-users
David Chelimsky
2010-Jul-17 21:53 UTC
[rspec-users] assigns and should eql don''t work as expected
On Jul 17, 2010, at 2:25 PM, Daniel Salmeron Amselem wrote:> On Jul 17, 9:41 am, David Chelimsky <dchelim... at gmail.com> wrote: >> On Jul 17, 2010, at 8:09 AM, David Chelimsky wrote: >>> On Jul 17, 2010, at 3:29 AM, Daniel Salmeron Amselem wrote: >> >>>> Today I''ve been writing some tests for a new rails 3 app, but after >>>> reading the doc fromhttp://rdoc.info/projects/rspec/rspec-expectations, >>>> I still can''t understand why the test doesn''t work. My setup is: >> >>>> rvm 0.1.41 >>>> ruby 1.9.2dev (2010-07-11 revision 28618) [x86_64-darwin10.4.0] -> >>>> ruby 1.9.2-rc2 >>>> rspec 2.0.0.beta.17 >>>> rspec-rails 2.0.0.beta.17 >>>> devise 1.1.rc2 >> >>>> This is the test for the controller: >> >>>> require ''spec_helper'' >> >>>> describe PeopleController do >> >>>> describe "routes" do >>>> it "should route to GET people#new" do >>>> {:get => "/people/new"}.should route_to(:controller => >>>> "people", :action => "new") >>>> end >>>> end >> >>>> describe "Methods" do >> >>>> before :each do >>>> @member = Factory(:member) >>>> sign_in @member >>>> @person = @member.build_person >>>> end >> >>>> it "should render form for a new person on GET people#new" do >>>> @member.should_receive(:build_person).and_return(@person) >> >>>> get :new >> >>>> assigns[:person].should eql(@person) >>>> response.should be_success >>>> response.should render_template("new") >>>> end >>>> end >> >>>> end >> >>>> And the controller: >> >>>> class PeopleController < ApplicationController >>>> before_filter :authenticate_member! >> >>>> def new >>>> @person = current_member.build_person >>>> end >> >>>> end >> >>>> When running the test I get: >> >>>> .F................. >> >>>> 1) PeopleController Methods should render form for a new person on GET >>>> people#new >>>> Failure/Error: assigns[:person].should eql(@person) >> >>>> expected #<Person id: nil, first_name: nil, last_name: nil, >>>> gender: nil, university: nil, year: nil, email: nil, phone: nil, >>>> house: nil, user_account_id: 126, user_account_type: "Member", >>>> home_town: nil, bio: nil, current_location: nil, high_school: nil, >>>> undergrad: nil, profession: nil, concentration: nil, created_at: nil, >>>> updated_at: nil> >>>> got #<Person id: nil, first_name: nil, last_name: nil, >>>> gender: nil, university: nil, year: nil, email: nil, phone: nil, >>>> house: nil, user_account_id: 126, user_account_type: "Member", >>>> home_town: nil, bio: nil, current_location: nil, high_school: nil, >>>> undergrad: nil, profession: nil, concentration: nil, created_at: nil, >>>> updated_at: nil> >> >>> Here''s how ActiveRecord defines == (to which it delegates from eql?) >> >>> http://github.com/rails/rails/blob/c6e20586372743ce200449bf0ac21aed04... >> >>> It returns false if the record has no id (!comparison_object.new_record?), even if all of the other attributes match. In order to get this to pass you have to actually save the object so it has an id, not just build it. >> >>> I discussed this with Rails core members a year or two ago and while they agreed this would make testing easier, there were two motivating arguments not to change it: a) conceptually, id-less records are not necessarily the same entity (this one is a bit fuzzy to me) and b) it''s a risky change given the amount of rails code in existence. >> >>> The other thing you can do is skip the stubbing and just say: >> >>> assigns(:person).should be_a_new(Person) >> >>> You could also write a custom matcher - something like match_new_record(other) that compares all of the attributes. Maybe it''s have_same_attributes_as: >> >>> assigns(:person).should have_same_attributes_as(@person) >> >>> I''d consider adding that to rspec-rails. Might be good to have a matcher with docs that explain all this to help avoid this sort of confusion in the future. >> >> Another option would be to add something like this: >> >> RSpec.configure do |c| >> c.treat_new_active_record_objects_as_equal >> end >> >> ... and have that override ==(other) on ActiveRecord::Base. It''s invasive, but you have to take an action to do the override. >> >> WDYT?> Thanks David, this was driving me crazy. In my opinion, I think will > be a great idea to add a new matcher to rspec-rails to do this kind of > comparison better than overriding == , and have total control of what > you want to test in each case. Anyways, thanks for helping me on this.I added an issue for this: http://github.com/rspec/rspec-rails/issues/#issue/131 Please share ideas there about what this matcher might look like. Cheers, David>>> WDYT?, >>> David >> >>>> (compared using eql?) >>>> # ./spec/controllers/people_controller_spec.rb:24:in `block (3 >>>> levels) in <top (required)>'' >> >>>> Finished in 2.29 seconds >>>> 19 examples, 1 failure >> >>>> The error with the full backtrace here:http://gist.github.com/479362 >> >>>> Which doesn''t seem to make sense. Any ideas? >> >>>> Thanks.