[rspec-users] controller spec with model that validates_uniqueness

Zach Dennis zach.dennis at gmail.com
Thu Aug 23 22:49:30 EDT 2007


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


More information about the rspec-users mailing list