It’s stuff like this that makes Rails and its accompanying toolset a pain in the ass.
If you want a spec to check that your controller rendered a specific thing, you can’t just mock the “controller.render” method. Why? In Rails-land, apparently controller.render is so magical that your mock expectation will receive no arguments, even if your controller passed arguments to the render method. It’ll say, “got: (no args)”
Instead, you have to use the RenderTemplateMatcher with have_rendered or rendered_template (they’re the same).
That, in turn, means you have to actually let the render call complete normally, which may violate the isolation of your controller spec.
Or, if you’re like me, it gives you “ActionView::MissingTemplate: Missing template” even though the template path is accurate. This has something to do with the EmptyTemplatePathSetDecorator mentioned in the error.
At this point, you search the internet until you discover that “By default, controller specs do not render views.” However, strangely, at the same time, “NOTE: unlike rspec-rails-1.x, the template must exist.” Still no good explanation why your completely valid template isn’t being found.
So then you try adding “render_views” to your test, but find that it’s configured to only accept production templates, not template files that you have in your spec directory. So now you have to provide mocks or stubs for all the data that your chosen production template needs. So obviously that’s not a good way to go.
So then you back out the “render_views” statement, and suddenly everything’s working. What?!
Turns out that by default Rspec-Rails doesn’t actually render any templates, but it does look for the template file in the standard file paths (app/views). So don’t try to outsmart Rspec by providing your own view specifically for your spec: it won’t be able to find it.
What a waste of time.
PS: Due to limitations of ActionController::TemplateAssertions$assert_template, which RenderTemplateMatcher delegates to, you can’t check both the template and the layout at the same time. You have to use two separate statements:
response.should have_rendered template
response.should have_rendered layout: layout