RSpec View testing: How to modify params? - ruby-on-rails

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.

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'})

That's because you shouldn't be using params in your views.
The best way I see it to use an helper.
<div>Sorted by <%= sorted_by %></div>
And in one of your helper files
def sorted_by
params[:sorted_by].capitalize
end
Then you can test your helpers quite easily (because in helpers tests, you can define the params request.

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

Another method of setting view params:
controller.request.path_parameters[:some_param] = 'a value'

Related

How to stub a flash in a RSpec spec?

In my view, I have a hidden_field_tag whose value is a flash set in the controller. In other words, the flow is as follows:
Controller:
def home
flash[:id] = 123
end
View:
<% form_tag(new_invitee_path) %>
<%= hidden_field_tag :referer, flash[:id] %>
<% end %>
Params submitted to new_invitee_path:
{ "referer" => "123" }
I can confirm that in manual testing this works appropriately, but I can't figure out how to stub appropriately.
In my test I have:
before do
#set flash
visit '/home'
fill_in "rest_of_form"
click_button "submit_form
end
Where below are the things I've tried to do for set flash and the error messages I get:
flash[:id] = 123
# OR
flash.now[:id] = 123
# both render error: undefined local variable or method `flash' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_1:0x007fc1040f7d60>
# Have also tried a tactic found online to set flash for response object like this:
visit '/home'
response.flash[:id] = 123
# OR
response.flash.now[:id] = 123
# both render error: undefined local variable or method `response' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_1:0x007fe118a38490>
#Have read online that it's a problem with the flash being sweeped, so I tried to stub out the sweep, but am unclear how to set the anonymous controller or whatever correctly
controller.instance_eval{flash.stub!(:sweep)}
flash[:id] = 123
# OR
flash.now[:id] = 123
# renders error: undefined local variable or method `flash' for nil:NilClass
Your spec is a feature spec, so the spec environment has no access to things like the flash. Don't try to work with the flash directly. Instead, ideally, test that the user's view of the app looks and/or behaves the way that it should if the flash value is set the way that it should be. I wouldn't just test that the hidden field is present in the form; I'd test that it has the effect that it should after the form is submitted. That's what feature specs are all about: testing that the application works as a whole from the user point of view.
If the flash value isn't ever used in the UI, just logged or stored in the database, it would be OK to test that the log line or model object has the value that's stored in the flash. (The user here is the admin who would look at the log or whatever later.) But if the flash does affect the UI, testing that is preferable.
This seems to work pretty well for me:
YourController.any_instance.stub(:flash) { {some: "thing" }}

How to pass an extra parameter to 'get' in Rails 2.3 functional testing

Fellow Overflowers,
There is a peculiar issue I am dealing with and can find no obvious solution:
I am passing a parameter at the end of a url:
http://localhost:3000/admin/patients/30/verify?unify=true
The parameter is not defined in my route which is defined like this:
verify_admin_patient GET /admin/patients/:id/verify(.:format) {:controller=>"admin/patients", :action=>"verify"}
What the controller does, it reads the params[:unify] and if it is true it verifies the details of a patient and adds them under the patient with the same name. If it is false it creates a new patient and adds the details.
However when it comes to testing is failing miserably and frustratingly:
test "should verify patient with unification" do
get :verify, :id => patients(:one).to_param, :unify => "true"
assert_equal "Successfully created patient.", #response.flash[:notice]
assert_response :redirect
end
The reason is that the test is not actually passing the unify parameter to the controller and thus it fails.
I have tried different ways to send the parameter like get '/admin/patients/30/verify?unify=true' or adding more info to the request like get '/admin/patients/30/verify?unify=true',:action => "verify", :controller => "admin/patients" with absolutely no vain.
Has anybody a clue about how to pass this kind of parameter on the test?
Thanks in advance.
get :verify, :id => patients(:one).to_param, :unify => "true"
not
get :verify, :id => patients(:one).to_param, :unify => true
Edit:
Head to your controller method, use
https://github.com/pry/pry
and put that as the first line of your method code. Run the tests, and it should pause there, indicating that you have indeed reached the point where the code is being run.
Then run the code in the method line by line, assuming you have no error and records are being created as expected, then the issue is with your test writing
Afaik you have to pass all parameters for the params hash in the second parameter in the test, like for example:
test "should verify patient with unification" do
get :verify, { :id => patients(:one).to_param, :unify => true }
assert_equal "Successfully created patient.", #response.flash[:notice]
assert_response :redirect
end
Does that work?

Setting up to invoke form helpers from Rails console

I'd like to experiment with using form helpers inside the Rails console, but my simple minded approach of doing extend ActionView::Helpers::FormHelper didn't work. For example, subsequently calling form_for resulted in the error NoMethodError: undefined method 'dom_class' for main:Object. Using include produces the same results.
Is there an "easy" way to enable me to call form helpers from the console? I'd prefer to do this without any dependency on controller or view files, if possible.
Peter, I'm happy to solve your problem.
For simple view helpers, it's very easy
> app.helper.link_to 'root', app.root_path
# <a href = ....> # as expected
However your specific case is not that easy as form_for needs view_context and then a controller instance to work.
# Get a controller instance at first. Must do it.
# Without this step `app.controller` is nil
> app.get app.root_path
# Use an instance variable but not variable!
> #user = User.last
# Get the view
# Note you need to take the trouble to define url manually
# because console can only reach path through app instance
> app.controller.view_context.form_for(#user, url: app.user_path(#user)){|f| f.input :name}
# Job done
You need to use include, not extend:
irb(main):007:0> include ActionView::Helpers::FormHelper
=> Object
irb(main):008:0> form_for
ArgumentError: wrong number of arguments (0 for 1)

Rspec2 + Rails4: Testing for displayed model fields in form partial

Question
I'd like to write tests that check the model fields that are displayed in my "show" and "form" partials. I succeeded for "show", not for "form".
Main constrain: The solution must be able to loop through an Array that contains each names of the model fields.
I believe this case can be interesting for anyone that is trying to shorten his test script files, while having many fields, and having a complete control over what's displayed and what's not, so I'll put some efforts trying to find a solution, with your help if you please :)
Form view
Nothing fancy
= form_for #user do |f|
= f.select :field_1, options_from_collection_for_select ...
= f.text_field :field_2
...
Actual situation
I found an easy way for the "show" partial, here is how my spec file looks like:
def user_fields_in_show_view
[:field_1, :field_2, ..., :field_n]
end
it 'display fields' do
user_fields_in_show_view.each do |field|
User.any_instance.should_receive(field).at_least(1).and_call_original
end
render
end
This works well.
-
But the exact same technique does not work in the "form" partial, using the same code
def user_fields_in_form_view # List of fields need to be different, because of the associations
[:field_1_id, :field_2, ..., :field_n]
end
it 'display fields' do
user_fields_in_form_view.each do |field|
User.any_instance.should_receive(field).at_least(1).and_call_original
end
render
end
It whines like this:
Failure/Error: Unable to find matching line from backtrace
Exactly one instance should have received the following message(s) but didn't: field1_id, field_2, ..., field_n
# Backtrace is long and shows only rspec/mock, rspec/core, rspec/rails/adapters, and spork files
What I tried so far
1- I commented out the stub part of my tests and output rendered to the console, to manually check what's generated by my view, and yes the fields are correctly generated.
2- I replaced User.any_instance by the model I assign to the view, error is slightly different but it still not working
it 'display fields' do
user = create :user
assign :user, user
user_fields_in_form_view.each do |field|
user.should_receive(field).at_least(1).and_call_original
end
render
end
Gives:
Failure/Error: user.should_receive(field).at_least(1).and_call_original
(#<User:0x0000000506e3e8>).field_1_id(any args)
expected: at least 1 time with any arguments
received: 0 times with any arguments
3- I change the code so the it is inside the loop, like this:
user_fields_in_form_view.each do |field|
it 'display fields' do
user = create :user
assign :user, user
user.should_receive(field).at_least(1).and_call_original
render
end
end
Same result as above
And I run out of options. I suspect the internals of FormBuilder to play a bad trick on me but I can't figure it out, I'm not very knowledgeable with those yet. Thanks for reading
I usually try to write unit test as simple as possible. Loops in unit tests don't add much readability and are not very good practice in general. I'd rewrite the test like this:
it 'should display user name and email' do
# note: `build` is used here instead of `create`
assign :user, build(:user, first_name: 'John', last_name: 'Doe', email: 'jd#example.com')
render
rendered.should have_content 'John'
rendered.should have_content 'Doe'
rendered.should have_content 'jd#example.com'
end
Thus, we're not limiting the view in how it should render the first and the last name. For example, if our view uses the following (bad) code in order to render user's full name, then your test will fail, but my test will work just fine, because it tests the behaviour of the view, not its internals:
<%= user.attributes.values_at('first_name', 'middle_name').compact.join(' ') %>
Moreover, multiple assertions in one test is a bad smell too. Going one step further, I'd replace this test with three smaller ones:
it "should display user's first name" do
assign :user, build(:user, first_name: 'John')
render
expect(rendered).to include 'John'
end
it "should display user's last name" do
assign :user, build(:user, last_name: 'Doe')
render
expect(rendered).to include 'Doe'
end
it "should display user's email" do
assign :user, build(:user, email: 'jd#example.com')
render
expect(rendered).to include 'jd#example.com'
end
========
UPD: Let's make it more dynamic in order to avoid tons of repetition. Tis doesn't answers why your spec fails, but hopefully represents working tests:
%i(first_name last_name email).each do |field|
it "should display user's #{field}" do
user = build(:user)
assign :user, user
render
expect(rendered).to include user.public_send(field)
end
end
In order to make these tests more reliable, make sure that user factory doesn't contain repetitive data.
I am not quite sure how you build your form, but if you use the form_for, simple_form_for or formtastic_form_for helpers, actually you are using a different object. You write something like (assume the basic form_for)
= form_for #user do |f|
and all methods are relayed to object f. Now f.object will point to #user, but is it a copy of #user or #user itself, I don't know. So I would expect that User.any_instance should work.
Regardless, when doing a view test, it is not important how the contents of a field are set, it is important that the contents of a field are set correctly. Suppose you build your forms yourself, you switch to another library to build your forms, and all your tests break, because it retrieves the data differently. And that should not matter.
So I am with #DNNX, and in your view tests you should test the content of the rendered HTML and not how the data is retrieved.

stubbing helpers using mocha

it "should have edit button if user has permission to edit" do
EntitiesHelper.stubs(:permission_to_edit_entity?).returns(true)
get :index
#entities[0..3].each do |entity|
response.should have_selector("form",
:method => "get",
:action => "/entities/edit/#{entity[:id]}") do |form|
form.should have_selector("input", :value => "Edit")
end
end
end
I am trying to write a simple test case which tests that an edit button is showed if the user has permission to edit. I am trying to use stubbing for this. However, it doesn't seem to work. The output view does not show the edit button next to every single entity which I would expect if the stubbing works. I am new to mocha and stubbing - I am doing something wrong here?
Thanks!
I assume EntitiesHelper is a plain-old rails helper that gets mixed into the controller - thus all it's instance methods (such as permission_to_edit_entity?) are available to the controller and views have access to these helper methods (via the controller) ... so You might stub the method on the controller :
controller.stubs(:permission_to_edit_entity?).returns(true)
in this particular case I would even consider changing the stub to mock since You expect the method to be called (although You're testing the button presence, it's good to know that the flow did not happen as expected) :
controller.expects(:permission_to_edit_entity?).returns(true)
but this of course is debatable and You should be fine either way ...

Resources