Since rspec 3.5 request specs are used to test controller behaviour plus route testing and the correct rendering of the view with the content.
The last part troubles me a bit since i do not understand the thin line of what goes in the view specs to what stays in the request specs.
On relishapp i found this piece:
expect(response.body).to include("Widget was successfully created.")
which tempted me of including the following in my request test:
describe "Index page" do
....
it 'includes a link to cars_parts' do
get "/car_overview"
expect(response.body).to include("car_parts_path")
end
....
end
This test fails.
In the view i use the link_to on the car_parts url_helper.
The failing test dumps the whole response.body String and i see the car_parts_path but rspec does not see it. How do i have to change my test to make it pass without the use of capybara since it is only useable with feature specs now.
And am i doing it correct after all or should this kind of test go somewhere else?
I think you might need to change the string to a method.
Before
expect(response.body).to include("car_parts_path")
After
expect(response.body).to include(car_parts_path) # Remove the quotes
Explanation
car_parts_path is a method in Rails, which will evaluate to the actual path. If you're using that URL helper in your view this should work.
But I thought the response body was a string?
Yup, it is. When you call car_parts_path, you're calling a Ruby method in the Rails framework.
car_parts_path will return a string. This string will be "/car_parts".
Basically, any time you see car_parts_path, imagine it's "/car_parts" as a string. Because the two are exactly equivalent.
In the test
So when you have this in the test:
expect(response.body).to include(car_parts_path) # car_parts_path is a method that returns "/car_parts" so it's the same as...
It's evaluates to the same as:
expect(response.body).to include("/car_parts")
In the view
In your ERB view you'll have:
<%= link_to "Car Parts", car_parts_path %>
This is evaluated to:
<%= link_to "Car Parts", "/car_parts" %>
This renders an a tag in your html:
Car Parts
Conclusion
You're checking for "/car_parts" in the response body and the view contains that string, the test now passes and you're all set!
Why did my original code not work?
Rails was looking for "car_parts_path" as a string - and the view doesn't contain that anywhere.
You were looking for the method name as a string rather than the string the method returns.
Any more questions, just ask!
Related
In a few of my controllers I have an action that does not have a corresponding route because it is accessed only via a render ... and return in other controller actions.
For example, I have an action
def no_such_page
# displays a generic error screen
end
In my RSpec controller test, how do I 'get' that method and look at the response body?
If I try:
get :no_such_page
response.status.should be(200)
it of course gives the error
No route matches {:controller=>"foo", :action=>"{:action=>:no_such_page}"}
Update
Looking back over your question, it doesn't make sense to me now since you say that you are only accessing this action via render ... and return, but render renders a view, not an action. Are you sure that you even need this action? I think a view spec is the place for this test.
Original answer
It doesn't make sense to test the response code of an action which will never be called via an HTTP request. Likewise get :no_such_page doesn't make sense as you can't "get" the action (there is no route to it), you can only call the method.
In that sense, the best way to test it would be to treat it just like any other method on a class, in this case the class being your controller, e.g. PostsController. So you could do something like this:
describe PostsController do
... other actions ...
describe "no_such_page" do
it "displays a generic error screen" do
p = PostsController.new
p.should_receive(:some_method).with(...)
p.no_such_page
end
end
end
But in fact, judging from what you've written, it sounds to me like your action has nothing in it, and you're just testing the HTML output generated by the corresponding view. If that's the case, then you really shouldn't be testing this in controller specs at all, just test it using a view spec, which is more appropriate for testing the content of the response body.
before :all do
Rails.application.routes.draw do
get '/no_such_page', to: "foo#no_such_page"
end
end
after :all do
Rails.application.reload_routes!
end
In a controller spec, I'm trying to test that a controller action calling render_remote_content renders the right partial with the correct.
Currently, I'm trying to do something like:
response.should render_template(partial: "path/to/_template", locals: {local_array: []})
This causes rspec to blow up in assert_template. I'd prefer to avoid stubbing render methods.
Anyone have any good ideas on how to accomplish this?
Rails 3.1
RSpec 2.7
When you are checking for locals, it appears that there is a bug in the rspec code, and you have to drop the underscore in the partial name.
Specifically:
response.should render_template(partial: "path/to/template", locals: {local_array: []})
will work, but your example will not.
EDIT:
This issue is actually in assert_template, since render_template delegates to that. This issue is being tracked here: https://github.com/rails/rails/issues/8516
Please don't tell me "search more" or other stuff cause all solutions for similar question fail.
Simple:
I have a functional tests. I want to make a simple get and see if proper content gets rendered
test "displays headline if user should see it" do
get :index
assert_match /headline/, response.body
end
test "doesn't display headline if user shouldn't see it" do
get :index
assert_no_match /headline/, response.body
end
and a simple view
<% if show_headline?(arg) %>
headline
<% end %>
and a helper:
module TheHelper
def show_headline?(arg)
arg ? hard_code_logic : even_harder_logic
end
end
so what I need is to do in test something like:
test "displays headline if user should see it" do
Something.stubs(:show_headline?).returns(true)
get :index
assert_match /headline/, response.body
end
test "doesn't display headline if user shouldn't see it" do
Something.stubs(:show_headline?).returns(false)
get :index
assert_no_match /headline/, response.body
end
The question is what is Something? I want to stub it cause I have helpers tested in unit/helpers.
After the get helper module gets remixed into the controller class. Please don't give me links to other answers, I read them (but of course I could have read the wrong ones) and they don't work for me. I use Rails 2.3.10 with mocha 0.9.8.
Things that don't work:
TheController.any_instance.stubs(:show_headline?)
ActionView::Base.any_instance...
#controller.stubs...
UPDATE:
the only mock that worked was:
<% self.stubs(:show_headline?).returns(true) >%
<% if show_headline?(arg) %>
headline
<% end %>
but of course I will not use that... maybe it is a clue
I have this route defined:
match 'me/save' => 'me#save', :via => :post
I only want this action method to work with the POST HTTP verb. How do I test to make sure it DOESN'T work with a GET verb? Or is this not even necessary? Currently I have this unit test:
test "save doesn't work with get" do
get :save
assert_response :error
end
However, when I run it, I get an error, rather than a failed test:
1) Error:
test_save_doesn't_work_with_get(MeControllerTest):
NoMethodError: You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.[]
app/controllers/me_controller.rb:19:in `save'
You could put this around the code you're testing (that generates the error):
assert_raise Exception { the code that does the getting }
Whether or not this is necessary, as you ask, depends on why you want to test this particular functionality.
If you're testing it to make sure that no one modifies your routes improperly (e.g. accidentally, opening up a security hole), then maybe it's worth it.
On the other hand, if you're writing the test to make sure that Rails is doing it's job, then you're just over testing.
Btw, you can just define post in your route instead of match via post, like :
post 'me/save' => 'me#save'
Now, to test that, you use post instead of get like :
test "save doesn't work with get" do
post :save, :id => ...
assert_response :error
end
where :id is just a posted parameter(if there is any).
However, your error seems to come from the fact that you have a nil object that tries to reference an attribute, somewhere in your save method.
And, btw, it's a bad idea to test Rails(this is what you do by testing match). It's already very extensively tested and it's mostly a waste of your time.
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'