Rspec view test with url parameters

3.3k views Asked by At

I have a page dashboard.html.erb that may be redirected to from several different controllers/actions. When it receives the redirect, it also receives several url parameters.

Controller code:

class PlansController
  def some_action
    redirect_to dashboard_path(show: "default", cool_array: ["test", "test"])
  end
end

class StaticpagesController
  def dashboard
  end
end

View code:

<% if cool_array %>
  <% cool_array.each do |a| %>
    <li><%= a %></li>
  <% end %>
<% end %>

<script>
var show = getParamFromURL("show")
// some sort of function that returns the value of the show param, in this case, "default"
if (show == "default") {
  $("#default").addClass("show");
} else {
  $("#other").addClass("hidden");
}
</script>  

Because these are redirects, controller testing with render_views will not work. Therefore, I had thought to construct my tests so that the controller spec tests that the right parameters are being passed, and the view spec tests that if certain parameters are present, the right css classes are present, or whatever. But in my view spec, I'm having trouble simulating the passing of parameters.

This seems to be the only other question out on this that I found: RSpec View testing: How to modify params?

But none of the solutions have worked for me; I'm not sure in this case I need a helper... which seems a bit contrived...

I have tried (from the linked SO question above):

before do
  controller.request.path_parameters[:cool_array] = ["test","test"]
end
# => ActionView::Template::Error:
#  undefined local variable or method `cool_array' for #<#<Class:0x007fe2d9815100>:0x007fe2d90a62e8>
# thrown by line in view file with <% if cool_array %>

before do
  view.stub(:params).and_return({cool_array: ["test", "test"]})
end
# => ActionView::Template::Error:
#  undefined local variable or method `cool_array' for #<#<Class:0x007fe2d9815100>:0x007fe2d90a62e8>
# thrown by line in view file with <% if cool_array %>

helper.params = {:cool_array => ["test", "test"]]}
# honestly not even sure this is right, because it can only go into a describe block (not a before, not an it) and then it throws:
# NoMethodError:
#   undefined method `params=' for #<#<Class:0x007fd7cc8f4930>:0x007fd7cc8a5060>

And they all fail with:

1

There are 1 answers

1
max On BEST ANSWER

Because these are redirects, controller testing with render_views will not work.

Nope, you are just doing it wrong.

If StaticpagesController#dashboard accepts url parameters you should test how it responds to said parameters in the controller spec for StaticpagesController.

RSpec.describe StaticpagesController, type: :controller do 
  describe 'GET #dashboard' do
    render_views
    it "does something with the x parameter" do
       get :dashboard, x: 'hello!'
       expect(response.body).to match 'hello!'
    end
  end
end

Similarly you should test that PlansController#some_action includes the correct parameters in the response.

I would argue that your view specs problems are telling you that you have a code smell in your views. In MVC your controllers are responsible for taking input and passing it to views and models. Isolating your views from dealing directly with parameters means that the parameter handling it not duplicated all over your app.

Use locals in your controllers to pass variables to your views and avoid using params in your views.

It makes for a better design and easier tests:

describe "rendering locals in a partial" do
  it "displays the widget" do
    widget = stub_model(Widget, :name => "slicer")
    render :partial => "widgets/widget.html.erb", :locals => {:widget => widget}
    rendered.should contain("slicer")
  end
end