Checking for multiple copies of a selector in RSpec - ruby-on-rails

I'm having a problem with one of my tests in RSpec. The test in question is supposed to test whether there is more than one post on the index page, by checking for the existence of more than one <article class="post"> tag on the page. My current implementation looks like this:
it "should have more than one post" do
get :index
response.should have_selector("article", :class => "post", :count => 2)
end
For some reason this test fails, despite showing multiple instances of the <article class="post"> tag in the debug output. The specific error it give is
Failures:
1) PostsController GET 'index' should have more than one posts
Failure/Error: response.should have_selector("article", :class => "post", :count => 2)
expected following output to contain a <article class='post'/> tag:
Note that if I remove the :count => 2 option the test passes without incident.
Does anyone know why this test is failing?

I'm guessing you are using Capybara under RSpec here or Webrat? With the count option your test is asserting that the response contains exactly 2 posts, rather than at least 2. Does the minimum option do what you want?
See https://github.com/jnicklas/capybara/blob/master/lib/capybara/node/matchers.rb and http://rubydoc.info/github/jnicklas/capybara/master/Capybara/Node/Matchers#has_selector%3F-instance_method

Related

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?

Capybara and Rspec: correct way to use within() and have_selector() together?

I use rspec 2.6.0 and Capybara 1.1.1 for acceptance testing.
With a view like the following:
<tr >
<td>Team 3 Name</td>
<td>true</td>
<td>Show</td>
<td>Edit</td>
<td>Deactivate</td>
</tr>
<tr >
<td>Team 4 Name</td>
<td>true</td>
<td>Show</td>
<td>Edit</td>
<td>Deactivate</td>
</tr>
I want to write an acceptance test that states: "Team 3 does NOT have the 'Deactivate' link." I expect the following to fail:
within('tr', :text => 'Team 3 Name') do |ref|
page.should_not have_selector('a', :text => 'Deactivate')
end
But it passes. To further test what is going on, I wrote the absurd:
lock = false
within('tr', :text => 'Team 3 Name') do |ref|
page.should have_selector('a', :text => 'Deactivate')
page.should_not have_selector('a', :text => 'Deactivate')
lock = true
end
lock.should be_true
Which passes as well.
I am assuming from this that the scope the have_selector() call is using is not limited by the within() block, but I am not sure why this is. The capybara documentation uses this pattern and does not seem to mention any gotchas.
What is the correct way to use within to limit the scope of my select?
Thank you.
/Salernost
Still learning Capybara myself, but have you tried have_link instead of have_selector? Also I don't think you need |ref|. For example:
lock = false
within('tr', :text => 'Team 3 Name') do # omit |ref|
page.should have_link('Deactivate')
page.should_not have_link('Deactivate')
lock = true
end
lock.should be_true
Update October 13, 2012
Having come a little further with Capybara, I see several potential issues here:
within may silently ignore the text field. You'll notice that the examples only show CSS or XPath finders without additional arguments.
If within does use text, it may not work here because you are asking it to look at the <tr>, but the text is in the <td>.
It's quite possible that the page subject still targets the entire page even if you are in a within block. The within examples are mostly about using fill_in or click. The exception is the example under Beware the XPath // trap.
As for creating a within block, you can either give your table rows unique ids and search for them using CSS, or you may be able to write a specific XPath targeting the first matching row.
The problem with the latter is that you want use the within on the <tr>, but the text you are using for your targeting is inside a <td> subelement. So for example, this XPath should find the table cell containing the text Team 3 Name but then you are only working within that first cell, not the whole row.
within(:xpath, "//tr/td[normalize-space(text())='Team 3 Name'") do
There are ways to "back up" to a parent element using XPath but I don't know how to do it and I've read that it's not good practice. I think your best bet here might be to just generate ids so your rows start like this:
<tr id="team_3">
then target them with a simple
within("tr#team_3")
I would also recommend Mark Berry's final approach he mentioned of adding id's to each of your table elements.
<tr id="team_3">
then target with
within("tr#team_3")
Capybara has given me issues when selecting by xpath in that it doesn't seem to work consistently, especially with CI services.
I also want to note on the same answer this section:
It's quite possible that the page subject still targets the entire page even if you are in a within block. The within examples are mostly about using fill_in or click. The exception is the example under Beware the XPath // trap.
This may have been the case in an older version, but in the current version of Capybara, calling page inside of a within block only inspects the part of the page targeted. So, using Mark's above example:
within("tr#team_3") do
expect(page).to have_content 'Team 3 Name'
# => true
expect(page).to have_content 'Team 4 Name'
# => false
end
have_selector seems to ignore :text and :content options. I had to use something like this instead:
within 'a' do
page.should have_content 'Deactivate'
end
The solution is to not use within method:
expect(page).to have_css('tr#team_3') do
without_tag('a', text: 'Deactivate')
end

Rails 2.3.x - How to stub a helper method (that gets called from a view) in a functional test (no RSpec)?

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

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:
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'

testing REST with shoulda and factory_girl - destroy

i'm developing test for REST using shoulda and factory_girl. Code below
context "on :delete to :destroy" do
setup do
#controller = NewsArticlesController.new
#request = ActionController::TestRequest.new
#response = ActionController::TestResponse.new
#news_article = Factory.create(:news_article)
end
should "destroy new NewsArticle" do
assert_difference('NewsArticle.count', -1) do
delete :destroy, :id => #news_article.id
end
end
should_redirect_to news_articles_path
end
as a result i see
1) Error:
test: on :delete to :destroy should redirect to index. (NewsArticlesControllerTest):
ArgumentError: block not supplied
c:/develop/ruby/lib/ruby/gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/action_controller/macros.rb:201:in `instance_eval'
c:/develop/ruby/lib/ruby/gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/action_controller/macros.rb:201:in `__bind_1248853182_16800
0'
c:/develop/ruby/lib/ruby/gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/context.rb:351:in `call'
c:/develop/ruby/lib/ruby/gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/context.rb:351:in `test: on :delete to :destroy should redirect to index. '
Could you tell me plz - whats wrong and how i can modify test to make them work right?
UPD: routes looks fine
news_articles GET /news(.:format) {:controller=>"news_articles", :action=>"index"}
The problem is with should_redirect_to which now uses block to evaluate the redirect code. Sadly, neither thoughtbot wiki, nor the readme at github reflect this and still contain the old examples.
The correct code is
should_redirect_to "news articles page" { news_articles_path }
where the first argument is just a textual description (it is not eval'd as with the older version) used to generate a test name, so you get a test name like 'should redirect to news articles page'
Maybe you should use a symbol and post method when calling delete:
assert_difference 'Article.count', -1 do
post :delete, :id => ...
end
(referenced from http://api.rubyonrails.org/classes/ActiveSupport/Testing/Assertions.html#M001427)
tkramar solution points in the right direction, but i've had to write the code as:
should_redirect_to("news articles page") { news_articles_path }
Also see the new manual at http://dev.thoughtbot.com/shoulda/classes/Shoulda/ActionController/Macros.html#M000015

Resources