I know that in Capybara, you can do something like this:
page.should have_css("ol li", :count => 2)
However, assuming that page has for instance only one matching element, the error is not very descriptive:
1) initial page load shows greetings
Failure/Error: page.should have_css("ol li", :count => 2)
expected css "ol li" to return something
Instead of this rather obscure error message, is there a way to write the assertion in such way that error output would be something like 'When matching 'ol li', expected: 2, found: 1'. Obviously I could make a custom logic myself for such a behaviour - I'm asking is there a way to do this 'out of the box'?
For what it's worth, I'm using Selenium driver and RSpec.
page.should have_css("ol li", :count => 2)
would not have been implemented already.
I like this much better.
expect(page).to have_selector('input', count: 12)
Well, as it seems there is no support out-of-the-box, I wrote this custom matcher:
RSpec::Matchers.define :match_exactly do |expected_match_count, selector|
match do |context|
matching = context.all(selector)
@matched = matching.size
@matched == expected_match_count
end
failure_message_for_should do
"expected '#{selector}' to match exactly #{expected_match_count} elements, but matched #{@matched}"
end
failure_message_for_should_not do
"expected '#{selector}' to NOT match exactly #{expected_match_count} elements, but it did"
end
end
Now, you can do stuff like:
describe "initial page load", :type => :request do
it "has 12 inputs" do
visit "/"
page.should match_exactly(12, "input")
end
end
and get output like:
1) initial page load has 12 inputs
Failure/Error: page.should match_exactly(12, "input")
expected 'input' to match exactly 12 elements, but matched 13
It does the trick for now, I will look into making this part of Capybara.
I think the following is simpler, gives fairly clear output and eliminates the need for a custom matcher.
page.all("ol li").count.should eql(2)
This then prints out on error:
expected: 2
got: 3
(compared using eql?)
(RSpec::Expectations::ExpectationNotMetError)
Edit: As pointed out by @ThomasWalpole, using all
disables Capybara's waiting/retrying, so the answer above by @pandaPower is much better.
How about this?
within('ol') do
expect( all('.opportunity_title_wrap').count ).to eq(2)
end
within
, it's calling .count
on the results of all
that disables waiting/retrying. By calling count
on the results of all
(which an empty "array" is a valid return for) you convert to an integer and compare it. If that comparison fails the expectation fails. If instead you pass the count option to one of Capybara's matchers, capybara will wait/retry finding the specified selector until the count option matches (or Capybara.default_max_wait_time expires).
The current (9/2/2013) best practice recommended by Capybara is the following (source):
page.assert_selector('p#foo', :count => 4)
The answer by @pandaPower is very good, but the syntax was slightly different for me:
expect(page).to have_selector('.views-row', :count => 30)
Success story sharing
have_css
:expect(page).to have_css('input', count: 12)