[rspec-users] controller spec with model that validates_uniqueness

Zach Dennis zach.dennis at gmail.com
Thu Aug 23 22:57:52 EDT 2007


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


More information about the rspec-users mailing list