Search code examples
ruby-on-railsruby-on-rails-3rspecrspec-rails

Rspec request spec examine response body


Since rspec 3.5 request specs are used to test controller behaviour plus route testing and the correct rendering of the view with the content. The last part troubles me a bit since i do not understand the thin line of what goes in the view specs to what stays in the request specs.

On relishapp i found this piece:

expect(response.body).to include("Widget was successfully created.")

which tempted me of including the following in my request test:

describe "Index page" do
  ....
  it 'includes a link to cars_parts' do
    get "/car_overview"      
    expect(response.body).to include("car_parts_path")
  end
  ....
end 

This test fails. In the view i use the link_to on the car_parts url_helper. The failing test dumps the whole response.body String and i see the car_parts_path but rspec does not see it. How do i have to change my test to make it pass without the use of capybara since it is only useable with feature specs now. And am i doing it correct after all or should this kind of test go somewhere else?


Solution

  • I think you might need to change the string to a method.

    Before

    expect(response.body).to include("car_parts_path")
    

    After

    expect(response.body).to include(car_parts_path) # Remove the quotes
    

    Explanation

    car_parts_path is a method in Rails, which will evaluate to the actual path. If you're using that URL helper in your view this should work.

    But I thought the response body was a string?

    Yup, it is. When you call car_parts_path, you're calling a Ruby method in the Rails framework.

    car_parts_path will return a string. This string will be "/car_parts".

    Basically, any time you see car_parts_path, imagine it's "/car_parts" as a string. Because the two are exactly equivalent.

    In the test

    So when you have this in the test:

    expect(response.body).to include(car_parts_path) # car_parts_path is a method that returns "/car_parts" so it's the same as...
    

    It's evaluates to the same as:

    expect(response.body).to include("/car_parts")
    

    In the view

    In your ERB view you'll have:

    <%= link_to "Car Parts", car_parts_path %>
    

    This is evaluated to:

    <%= link_to "Car Parts", "/car_parts" %>
    

    This renders an a tag in your html:

    <a href="/car_parts">Car Parts</a>
    

    Conclusion

    You're checking for "/car_parts" in the response body and the view contains that string, the test now passes and you're all set!

    Why did my original code not work?

    Rails was looking for "car_parts_path" as a string - and the view doesn't contain that anywhere.

    You were looking for the method name as a string rather than the string the method returns.

    Any more questions, just ask!