Reputation: 3416
I am trying to test my views with RSpec. The particular view that is causing me troubles changes its appearance depending on a url parameter:
link_to "sort>name", model_path(:sort_by => 'name')
which results in http://mydomain/model?sort_by=name
My view then uses this parameter like that:
<% if params[:sort_by] == 'name' %>
<div>Sorted by Name</div>
<% end %>
The RSpec looks like this:
it "should tell the user the attribute for sorting order" do
#Problem: assign params[:sort_for] = 'name'
render "/groups/index.html.erb"
response.should have_tag("div", "Sorted by Name")
end
I would like to test my view (without controller) in RSpec but I can't get this parameter into my params
variable. I tried assign
in all different flavours:
assign[:params] = {:sort_by => 'name'}
assign[:params][:sort_by] = 'name'
no success so far. Every idea is appreciated.
Upvotes: 17
Views: 18083
Reputation: 32629
Canonical answer:
To use params in views from within view specs, if your view has params[:id]
, then somewhere in your spec do:
controller.extra_params = { id: widget.id }
Documentation: https://rspec.info/features/6-0/rspec-rails/view-specs/view-spec
However, it is also a good idea to not reference params
in your views.
To get them out of your views you could use a helper, like this:
<div>Sorted by <%= sorted_by %></div>
And in one of your helper files
def sorted_by
params[:sorted_by].capitalize
end
Unfortunately, you really shouldn't be referencing params
in helpers either. Don't reference what you don't own, ideally.
A better idea would be to use the Presenter Pattern, and a good example of a tool for that is ViewComponent.
Upvotes: 13
Reputation: 584
The accepted answer is good, and it is bad to use params in views... but there is (now) a documented way to do this, and as such the accepted answer doesn't actually answer the question.
controller.extra_params = { id: widget.id }
See: https://rspec.info/features/6-0/rspec-rails/view-specs/view-spec/
Upvotes: 0
Reputation: 983
If its a controller test then it would be
controller.stub!(:params).and_return {}
If its a helper test then it would be:
helper.stub!(:params).and_return {}
And its a view test it would be:
view.stub!(:params).and_return {}
If you get warning like below.
Deprecation Warnings:
Using `stub` from rspec-mocks' old `:should` syntax without explicitly enabling the syntax is deprecated. Use the new `:expect` syntax or explicitly enable `:should` instead. Called from /home/akbarbin/Documents/Office/projects/portfolio/spec/views/admin/waste_places/new.html.erb_spec.rb:7:in `block (2 levels) in <top (required)>'.
If you need more of the backtrace for any of these deprecations to
identify where to make the necessary changes, you can configure
`config.raise_errors_for_deprecations!`, and it will turn the
deprecation warnings into errors, giving you the full backtrace.
1 deprecation warning total
Finished in 4.86 seconds (files took 4.72 seconds to load)
You can change it into
allow(view).to receive(:params).and_return({sort_by: 'name'})
Upvotes: 38
Reputation: 5042
Another method of setting view params:
controller.request.path_parameters[:some_param] = 'a value'
Upvotes: 3
Reputation: 139
The easy way is to just do this:
helper.params = {:foo => '1', :bar => '2'}
But in general it's better to be more integration-y and not "stub" values when it's feasible. So I prefer to use controller tests with integrate_views. Then you can specify your params to the get, and test that the entire flow works, from sending params to the controller, to having them processed by the controller, and finally to rendering.
I also generally prefer to pull out view logic into helpers, which can be easier to test.
For instance, say I have a helper called selection_list, which returns a Hash whose "selected_preset" key relies on params[:selected_preset], and defaults to 42 if an empty value is specified for the param.
Here's a controller test where we've called integrate_views (you could of course do the same thing with an actual view test, if you're into that).
describe '#show' do
describe 'selected_preset' do
it 'should default to 42 if no value was entered' do
get :show, :params => {:selected_preset => ''}
response.template.selection_list[:selected_preset].should == 42
This integration test will alert me if some part of this functionality breaks. But I also would ideally like to have some unit tests to help me pinpoint that breakage.
I'll start by having the helper use an instance variable instead of directly accessing params. I'll change the above code by adding a single line directly below the get, as follows:
describe '#show' do
describe 'selected_preset' do
it 'should default to 42 if no value was entered' do
get :show, :params => {:selected_preset => ''}
assigns[:selected_preset].should == 42 # check instance variable is set
response.template.selection_list[:selected_preset].should == 42
Now I also can easily perform a helper unit test:
describe MyHelper do
describe '#selection_list' do
it 'should include the selected preset' do
assigns[:selected_preset] = 3
helper.selection_list[:selected_preset].should == 3
Upvotes: 5