s.ross
2007-Aug-23 17:57 UTC
[rspec-users] controller spec with model that validates_uniqueness
I want to use mocks and stubs to test the controller, but am having trouble getting my validation not to trigger. Here''s the code: # spec: Image.stub!(:find).and_return(@image) @image.should_receive(:save!).once.with(:any_args) put :update, :id => @image.id, :category_id => @category.id, :image => {:name => ''test'', :image_number => 13554} #model validates_presence_of :name validates_uniqueness_of :name, :allow_nil => true # rspec output ActiveRecord::RecordInvalid in ''ImagesController should update a database record when it receives a PUT'' Validation failed: Name has already been taken, Image number has already been taken It seems AR is not detecting that this is an edit/update that will not cause a uniqueness conflict. I believe the code in the model needs to remain in place because I don''t want a user creating a name conflict by editing an existing name into one that already exists in the database. Any thoughts on how better to spec this? Thanks
Courtenay
2007-Aug-24 00:11 UTC
[rspec-users] controller spec with model that validates_uniqueness
Does this work? Image.stub!(:validates_uniqueness_of).and_return(true) On 8/23/07, s.ross <cwdinfo at gmail.com> wrote:> I want to use mocks and stubs to test the controller, but am having > trouble getting my validation not to trigger. Here''s the code: > > # spec: > > Image.stub!(:find).and_return(@image) > @image.should_receive(:save!).once.with(:any_args) > put :update, :id => @image.id, :category_id => > @category.id, :image => {:name => ''test'', :image_number => 13554} > > #model > > validates_presence_of :name > validates_uniqueness_of :name, :allow_nil => true > > # rspec output > > ActiveRecord::RecordInvalid in ''ImagesController should update a > database record when it receives a PUT'' > Validation failed: Name has already been taken, Image number has > already been taken > > > It seems AR is not detecting that this is an edit/update that will > not cause a uniqueness conflict. I believe the code in the model > needs to remain in place because I don''t want a user creating a name > conflict by editing an existing name into one that already exists in > the database. Any thoughts on how better to spec this? > > Thanks > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
Daniel N
2007-Aug-24 01:53 UTC
[rspec-users] controller spec with model that validates_uniqueness
On 8/24/07, Courtenay <court3nay at gmail.com> wrote:> > Does this work? > > Image.stub!(:validates_uniqueness_of).and_return(true) > > > On 8/23/07, s.ross <cwdinfo at gmail.com> wrote: > > I want to use mocks and stubs to test the controller, but am having > > trouble getting my validation not to trigger. Here''s the code: > > > > # spec: > > > > Image.stub!(:find).and_return(@image) > > @image.should_receive(:save!).once.with(:any_args) > > put :update, :id => @image.id, :category_id => > > @category.id, :image => {:name => ''test'', :image_number => 13554} > > > > #model > > > > validates_presence_of :name > > validates_uniqueness_of :name, :allow_nil => true > > > > # rspec output > > > > ActiveRecord::RecordInvalid in ''ImagesController should update a > > database record when it receives a PUT'' > > Validation failed: Name has already been taken, Image number has > > already been taken > > > > > > It seems AR is not detecting that this is an edit/update that will > > not cause a uniqueness conflict. I believe the code in the model > > needs to remain in place because I don''t want a user creating a name > > conflict by editing an existing name into one that already exists in > > the database. Any thoughts on how better to spec this? > > > > Thanks > > _______________________________________________Why use a real image at all? I usually use mock_model on these and then stub/mock the specific calls in the controller method. This way your not testing the model at all. Just the controller. Of course, they can get pretty complex with all the stubbing etc. Image.stub!(:find).and_return(@image) @image.should_receive(:save!).once.with(:any_args) put :update, :id => @image.id, :category_id => @category.id, :image => {:name => ''test'', :image_number => 13554} could become. @category = mock_model( Category, :id => 1 ) # Not sure where this one is used other than the call to put @image = mock_model( Image, :id => 2 ) @image.should_receive( :save! ).once.with( :any_args ).and_return( true ) Image.stub!( :find ).and_return( @image ) put :update, :id => @image.id, :category_id => @category.id, :image => {:name => ''test'', :image_number => 13554} This way you''re not reaching into the model to check your controller. HTH Daniel -------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/rspec-users/attachments/20070824/7a761815/attachment.html
Zach Dennis
2007-Aug-24 02:49 UTC
[rspec-users] controller spec with model that validates_uniqueness
On 8/23/07, Daniel N <has.sox at gmail.com> wrote:> > > > On 8/24/07, Courtenay <court3nay at gmail.com> wrote: > > Does this work? > > > > Image.stub!(:validates_uniqueness_of).and_return(true) > > > > > > On 8/23/07, s.ross <cwdinfo at gmail.com> wrote: > > > I want to use mocks and stubs to test the controller, but am having > > > trouble getting my validation not to trigger. Here''s the code: > > > > > > # spec: > > > > > > Image.stub!(:find).and_return(@image) > > > @image.should_receive(:save!).once.with(:any_args) > > > put :update, :id => @image.id, :category_id => > > > @category.id, :image => {:name => ''test'', :image_number => 13554} > > > > > > #model > > > > > > validates_presence_of :name > > > validates_uniqueness_of :name, :allow_nil => true > > > > > > # rspec output > > > > > > ActiveRecord::RecordInvalid in ''ImagesController should update a > > > database record when it receives a PUT'' > > > Validation failed: Name has already been taken, Image number has > > > already been taken > > > > > > > > > It seems AR is not detecting that this is an edit/update that will > > > not cause a uniqueness conflict. I believe the code in the model > > > needs to remain in place because I don''t want a user creating a name > > > conflict by editing an existing name into one that already exists in > > > the database. Any thoughts on how better to spec this? > > > > > > Thanks > > > _______________________________________________ > > Why use a real image at all? I usually use mock_model on these and then > stub/mock the specific calls in the controller method. This way your not > testing the model at all. Just the controller. Of course, they can get > pretty complex with all the stubbing etc. > > Image.stub!(:find).and_return(@image) > @image.should_receive(:save!).once.with(:any_args) > put :update, :id => @image.id, :category_id => > @category.id, :image => {:name => ''test'', :image_number => 13554} > > could become. > > @category = mock_model( Category, :id => 1 ) # Not sure where this one is > used other than the call to put > @image = mock_model( Image, :id => 2 ) > @image.should_receive( :save! ).once.with( :any_args ).and_return( true ) > Image.stub!( :find ).and_return( @image ) > > put :update, :id => @image.id, :category_id => > @category.id, :image => {:name => ''test'', :image_number => 13554} > > This way you''re not reaching into the model to check your controller.You can even go as far as removing all of that ActiveRecord interaction from your controller. @image = mock("image") Image.should_receive(:update_image).with(@image) put :update, :id => 1, :category_id => 2, :image => @image You would also want to test a failure case I''m sure (if you redirected...): Image.stub!(:update_image).and_raise(ActiveRecord::ActiveRecordError) put :update, :id => 1, :category_id => 2, :image => @image response.should be_redirect response.should redirect_to(:action => "other_action") This keeps your controller a little cleaner and pushes down the interaction of working with ActiveRecord models to a minimum. Now you can put the code that actually handles an update in your model class and your controller doesn''t have to worry about how it is saved, it either works or raises an exception, Zach
Pat Maddox
2007-Aug-24 02:55 UTC
[rspec-users] controller spec with model that validates_uniqueness
On 8/23/07, s.ross <cwdinfo at gmail.com> wrote:> I want to use mocks and stubs to test the controller, but am having > trouble getting my validation not to trigger. Here''s the code: > > # spec: > > Image.stub!(:find).and_return(@image) > @image.should_receive(:save!).once.with(:any_args) > put :update, :id => @image.id, :category_id => > @category.id, :image => {:name => ''test'', :image_number => 13554} > > #model > > validates_presence_of :name > validates_uniqueness_of :name, :allow_nil => true > > # rspec output > > ActiveRecord::RecordInvalid in ''ImagesController should update a > database record when it receives a PUT'' > Validation failed: Name has already been taken, Image number has > already been taken > > > It seems AR is not detecting that this is an edit/update that will > not cause a uniqueness conflict. I believe the code in the model > needs to remain in place because I don''t want a user creating a name > conflict by editing an existing name into one that already exists in > the database. Any thoughts on how better to spec this? > > Thanks > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >As others have said, return a mock object instead of an actual AR object. You don''t really care what the underlying implementation is, only that particular methods get called. Pat
Zach Dennis
2007-Aug-24 02:57 UTC
[rspec-users] controller spec with model that validates_uniqueness
Actually better then an exception On 8/23/07, Zach Dennis <zach.dennis at gmail.com> wrote:> On 8/23/07, Daniel N <has.sox at gmail.com> wrote: > > > > > > > > On 8/24/07, Courtenay <court3nay at gmail.com> wrote: > > > Does this work? > > > > > > Image.stub!(:validates_uniqueness_of).and_return(true) > > > > > > > > > On 8/23/07, s.ross <cwdinfo at gmail.com> wrote: > > > > I want to use mocks and stubs to test the controller, but am having > > > > trouble getting my validation not to trigger. Here''s the code: > > > > > > > > # spec: > > > > > > > > Image.stub!(:find).and_return(@image) > > > > @image.should_receive(:save!).once.with(:any_args) > > > > put :update, :id => @image.id, :category_id => > > > > @category.id, :image => {:name => ''test'', :image_number => 13554} > > > > > > > > #model > > > > > > > > validates_presence_of :name > > > > validates_uniqueness_of :name, :allow_nil => true > > > > > > > > # rspec output > > > > > > > > ActiveRecord::RecordInvalid in ''ImagesController should update a > > > > database record when it receives a PUT'' > > > > Validation failed: Name has already been taken, Image number has > > > > already been taken > > > > > > > > > > > > It seems AR is not detecting that this is an edit/update that will > > > > not cause a uniqueness conflict. I believe the code in the model > > > > needs to remain in place because I don''t want a user creating a name > > > > conflict by editing an existing name into one that already exists in > > > > the database. Any thoughts on how better to spec this? > > > > > > > > Thanks > > > > _______________________________________________ > > > > Why use a real image at all? I usually use mock_model on these and then > > stub/mock the specific calls in the controller method. This way your not > > testing the model at all. Just the controller. Of course, they can get > > pretty complex with all the stubbing etc. > > > > Image.stub!(:find).and_return(@image) > > @image.should_receive(:save!).once.with(:any_args) > > put :update, :id => @image.id, :category_id => > > @category.id, :image => {:name => ''test'', :image_number => 13554} > > > > could become. > > > > @category = mock_model( Category, :id => 1 ) # Not sure where this one is > > used other than the call to put > > @image = mock_model( Image, :id => 2 ) > > @image.should_receive( :save! ).once.with( :any_args ).and_return( true ) > > Image.stub!( :find ).and_return( @image ) > > > > put :update, :id => @image.id, :category_id => > > @category.id, :image => {:name => ''test'', :image_number => 13554} > > > > This way you''re not reaching into the model to check your controller. > > You can even go as far as removing all of that ActiveRecord > interaction from your controller. > > @image = mock("image") > Image.should_receive(:update_image).with(@image) > put :update, :id => 1, :category_id => 2, :image => @image > > You would also want to test a failure case I''m sure (if you redirected...): > > Image.stub!(:update_image).and_raise(ActiveRecord::ActiveRecordError) > put :update, :id => 1, :category_id => 2, :image => @image > response.should be_redirect > response.should redirect_to(:action => "other_action") > > This keeps your controller a little cleaner and pushes down the > interaction of working with ActiveRecord models to a minimum. Now you > can put the code that actually handles an update in your model class > and your controller doesn''t have to worry about how it is saved, it > either works or raises an exception,If you are only using Category in the test then go with Daniel''s first suggestion. If you only need a numeric id which represents a Category don''t create a dummy mock that will only be used in your test. If your controller has to find a Category and an Image to update an image in your controller then I think my approach (minus the exception, use true/false return values instead on update_image) makes sense. Zach
s.ross
2007-Aug-24 03:13 UTC
[rspec-users] controller spec with model that validates_uniqueness
I left out a key piece of my spec code in the original question: before(:each) do @category = mock_model(Category) Category.should_receive(:find).any_number_of_times.and_return (@category) @image = mock_model(Image) @image_category = mock_model(ImageCategory) end For this simple controller (and they are *all* supposed to be simple, right?), I think taking AR out completely might work. It''s all working now... Thanks for all responses. BTW: Courtenay, great that you''re using rSpec on the Caboose Sample App. On Aug 23, 2007, at 7:57 PM, Zach Dennis wrote:> Actually better then an exception > > On 8/23/07, Zach Dennis <zach.dennis at gmail.com> wrote: >> On 8/23/07, Daniel N <has.sox at gmail.com> wrote: >>> >>> >>> >>> On 8/24/07, Courtenay <court3nay at gmail.com> wrote: >>>> Does this work? >>>> >>>> Image.stub!(:validates_uniqueness_of).and_return(true) >>>> >>>> >>>> On 8/23/07, s.ross <cwdinfo at gmail.com> wrote: >>>>> I want to use mocks and stubs to test the controller, but am >>>>> having >>>>> trouble getting my validation not to trigger. Here''s the code: >>>>> >>>>> # spec: >>>>> >>>>> Image.stub!(:find).and_return(@image) >>>>> @image.should_receive(:save!).once.with(:any_args) >>>>> put :update, :id => @image.id, :category_id => >>>>> @category.id, :image => {:name => ''test'', :image_number => 13554} >>>>> >>>>> #model >>>>> >>>>> validates_presence_of :name >>>>> validates_uniqueness_of :name, :allow_nil => true >>>>> >>>>> # rspec output >>>>> >>>>> ActiveRecord::RecordInvalid in ''ImagesController should update a >>>>> database record when it receives a PUT'' >>>>> Validation failed: Name has already been taken, Image number has >>>>> already been taken >>>>> >>>>> >>>>> It seems AR is not detecting that this is an edit/update that will >>>>> not cause a uniqueness conflict. I believe the code in the model >>>>> needs to remain in place because I don''t want a user creating a >>>>> name >>>>> conflict by editing an existing name into one that already >>>>> exists in >>>>> the database. Any thoughts on how better to spec this? >>>>> >>>>> Thanks >>>>> _______________________________________________ >>> >>> Why use a real image at all? I usually use mock_model on these >>> and then >>> stub/mock the specific calls in the controller method. This way >>> your not >>> testing the model at all. Just the controller. Of course, they >>> can get >>> pretty complex with all the stubbing etc. >>> >>> Image.stub!(:find).and_return(@image) >>> @image.should_receive(:save!).once.with(:any_args) >>> put :update, :id => @image.id, :category_id => >>> @category.id, :image => {:name => ''test'', :image_number => 13554} >>> >>> could become. >>> >>> @category = mock_model( Category, :id => 1 ) # Not sure where >>> this one is >>> used other than the call to put >>> @image = mock_model( Image, :id => 2 ) >>> @image.should_receive( :save! ).once.with( :any_args ).and_return >>> ( true ) >>> Image.stub!( :find ).and_return( @image ) >>> >>> put :update, :id => @image.id, :category_id => >>> @category.id, :image => {:name => ''test'', :image_number => 13554} >>> >>> This way you''re not reaching into the model to check your >>> controller. >> >> You can even go as far as removing all of that ActiveRecord >> interaction from your controller. >> >> @image = mock("image") >> Image.should_receive(:update_image).with(@image) >> put :update, :id => 1, :category_id => 2, :image => @image >> >> You would also want to test a failure case I''m sure (if you >> redirected...): >> >> Image.stub!(:update_image).and_raise >> (ActiveRecord::ActiveRecordError) >> put :update, :id => 1, :category_id => 2, :image => @image >> response.should be_redirect >> response.should redirect_to(:action => "other_action") >> >> This keeps your controller a little cleaner and pushes down the >> interaction of working with ActiveRecord models to a minimum. Now you >> can put the code that actually handles an update in your model class >> and your controller doesn''t have to worry about how it is saved, it >> either works or raises an exception, > > If you are only using Category in the test then go with Daniel''s first > suggestion. If you only need a numeric id which represents a Category > don''t create a dummy mock that will only be used in your test. > > If your controller has to find a Category and an Image to update an > image in your controller then I think my approach (minus the > exception, use true/false return values instead on update_image) makes > sense. > > Zach > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users