Today I Learned

A Hashrocket project

Ready to join Hashrocket? Find Openings here and apply today.

RSpec's Render Template is Permissive

In a controller test, when you expect a request to render a template, you’d write something like this:

expect(response).to render_template(new_user_path)

If your controller action looked like the following, the expectation would pass:

class UsersController < ActionController::Base
  def action
    ...
    render :new
  end
end

render_template delegates to ActionController::TemplateAssertions#assert_template. What I didn’t know, however, is how permissive (magical) it is.

Let’s say instead of the action’s render :new you returned a json object with the template, rendered to a string, as one of its values:

def action
  response_body = render_to_string(layout: false, template: "users/new.html.haml")
  render json: { page: response_body }
end

expect(response).to render_template(new_user_path) still passess!!!

Also if you say expect(response).to render_template("new.html.haml"), it also passes. How does it know you meant the user’s new.html.haml and not some other arbitrary one? Maybe because you’re in the UsersControllerSpec. You can also expect...render_template for templates unrelated to the controller’s scope.

I suspect that this permissiveness is caused by Rails registering the rendering of templates, irrespective of whether they are rendered to strings or directly as a response to a controller action. expect...render_template maybe then looks through the registered renders 🤷🏾‍♂️. Either way, it’s magical!