sebastiangeiger
sebastiangeiger

Reputation: 3416

RSpec View testing: How to modify params?

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:

no success so far. Every idea is appreciated.

Upvotes: 17

Views: 18083

Answers (5)

Damien MATHIEU
Damien MATHIEU

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

Peter H. Boling
Peter H. Boling

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

fernyb
fernyb

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

CalebHC
CalebHC

Reputation: 5042

Another method of setting view params:

controller.request.path_parameters[:some_param] = 'a value'

Upvotes: 3

user210977
user210977

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

Related Questions