← Back to Upcase

Testing Fundamentals - Write a controller spec

(Andy Waite) #22

@anthonylebrun this will work in some cases, but if your controller attempts a redirect in the form of redirect_to @person, it will fail because Rails needs to know the type of the object.

(Pierre Lebrun) #23

@andyw8 Makes sense.

(Geoff Harcourt) #24

@anthonylebrun and @andyw8, sometimes I’ll use FactoryGirl’s build_stubbed strategy here so that I get a fast, non-database-touching, stubbed object, but it responds correctly to the methods that ActionController queries to get the object type, etc.

(Pierre Lebrun) #25

Nice, thanks for that resource!

(Andrew Charles Potter Kelley) #26

Did you get a reply to this?

(Patrick) #27
 context "when person is invalid" do
    it "renders the 'new' template" do
    # Use a partial-double to create an invalid person object
    person = Person.create
    allow(person).to receive(:save).and_return(false)
    allow(Person).to receive(:new).and_return(person)

    post :create, person: {:first_name => 'foo'}
        expect(response).to render_template(:new)

It is my understanding that what is going on here is as follows:

  1. We send what appears to be valid :first_name in our POST request, but it still redirects us to the :new template.
  2. This is due to the above partial-double: when the controller runs the #create action, it’s call to Person.new from the create action is using the stub above, meaning that no matter what you send to it, it is instead going to return the person object with a nil :first_name.
  3. We could have technically provided :first_name to person = Person.create since we don’t care about the validation: it’s being handled by the stubbed :save method we set to return false.
  4. Once the #create action calls @person.save, it will use the stubbed :save method and always return false.
  5. Since it always returns false, it will always render_template :new

Is my understanding of it correct? Does the :first_name we pass in the params truly never hit the object, instead being overridden by whatever we provided in the person = Person.create in our partial-double?

(Chris Toomey) #28

Hi @patrickmontalto, I believe you are correct in your interpretation of what is happening here. I would likely leave off the person data in the post to make the spec more clear, and extract the stubbing to a helper method, but otherwise this is similar to what I might do when testing controller behavior to avoid redundant testing of the validation logic.

(wakelank) #29

We stub out the save method to lose the dependency on knowing model’s validations, right? But the first step in the test is to create a valid person. So if the validations change in the future we still need to update this test. We could stub out Person.create, but we need the person to exist in the database in order to test that the redirect goes to the person show page. This seems weird to me. Am I missing something?

BTW, this has been a very educational thread. Thanks to all.

(Geoff Harcourt) #30

@wakelank If you’re stubbing out the .new/.create/.save methods on the model, you’re effectively bypassing validations. In a controller test you’re likely looking to verify very controller-specific functionality such as “where will I get redirected if this is successful vs. unsuccessful?”

If you need to have a correct person in the database for a particular test (rather than stubbing out .find and returning a test double), using a factory approach such as FactoryGirl is probably the best solution, as the factory will always have the correct knowledge of what a Person needs. I think you can stub methods out in this exercise enough to avoid needing another gem, but it’s a nice approach when working on a fuller application.