2

I'm a bit confused with the behavior of rpsec tests involving controller methods that affect the DB. I have seen many examples of rspec tests that involve POST's and DELETE's where people check to see that an object was created or deleted. In most of these tests people are able to just check that the count of the model in the DB has increased or descreased with a tests such as:

delete :clear_photos, :id => @album.id
@album.photos.size.should == 0 

or with lambdas:

lambda {delete :destroy, :id => @photo.id}.should change(@album.photos, :size).by(-1)

The syntax isn't perfect in the last example but my point is that in my experience, I have needed to call reload on the object in order for any of these tests to pass, but for some reason, other are able to make them work without explicitly calling reload. Something about calling reload every time I am testing a db create/destroy action seems fishy to me.

Can anyone help me understand what's going on? Thanks!

ACTUAL CODE UPDATE

it "should clear all photos for an album" do
  @album = Factory(:album, :photos => [Factory(:photo), Factory(:photo)])
  delete :clear, :album_id => @album.id
  @album.photo_count.should == 0
end

I get this response:

'PhotosController#clear should clear all photos for an album' FAILED
expected: 0,
     got: 2 (using ==)
./spec/controllers/photos_controller_spec.rb:17:

If I reload the @album before calling photo_count though, it works.

Danny
  • 4,724
  • 6
  • 42
  • 55
  • Can you give an example of failing code your having trouble with? You shouldn't need to call reload unless you've saved the result before performing the request. – nmunson May 05 '11 at 03:40
  • The code that you gave seems to be behaving as it should since your assertion is on a variable that you set prior to the request. Maybe you could give some actual code that you've seen that doesn't call reload. – monocle May 05 '11 at 04:00
  • I've posted the actual code that fails. Let me know if you need something else! – Danny May 05 '11 at 04:01
  • Again, you're using an instance variable that is set somewhere before the test that you show. By reloading it, then you're forcing another call to the database so your test can pass. This is the way it should be. What is the other person's code that supposedly doesn't need to reload the instance variable? – monocle May 05 '11 at 04:12
  • @monocle I moved the model creation into the test itself and it still failed. Shouldn't the delete method act like a normal delete request and allow me to check the db without calling reload? – Danny May 05 '11 at 05:40
  • 1
    I would like to point out that testing a model state in a controller spec is not a very good practice, because it violates the isolation of a unit test. You should instead test if a controller response is appropriate for the current scenario. – orion3 May 05 '11 at 16:33
  • @snitko your answer helped! I moved the logic into a model and refactored my tests and everything works :) If you write that answer I'll be more than happy to give it the thumbs up. – Danny May 05 '11 at 20:55

1 Answers1

5

I would like to point out that testing a model state in a controller spec is not a very good practice, because it violates the isolation of a unit test. You should instead test if a controller response is appropriate for the current scenario.

orion3
  • 9,797
  • 14
  • 67
  • 93