How to assert number of elements using Capybara with proper error message?

RspecDslCapybara

Rspec Problem Overview


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.

Rspec Solutions


Solution 1 - Rspec

I like this much better.

expect(page).to have_selector('input', count: 12)

https://github.com/jnicklas/capybara/blob/415e2db70d3b19b46a4d3d0fe62f50400f9d2b61/spec/rspec/matchers_spec.rb

Solution 2 - Rspec

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.

Solution 3 - Rspec

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)

Solution 4 - Rspec

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

Solution 5 - Rspec

The current (9/2/2013) best practice recommended by Capybara is the following (source):

page.assert_selector('p#foo', :count => 4)

Solution 6 - Rspec

The answer by @pandaPower is very good, but the syntax was slightly different for me:

expect(page).to have_selector('.views-row', :count => 30)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionmerrypranksterView Question on Stackoverflow
Solution 1 - RspecpandaPowderView Answer on Stackoverflow
Solution 2 - RspecmerrypranksterView Answer on Stackoverflow
Solution 3 - RspecRichardView Answer on Stackoverflow
Solution 4 - RspecConstant MeiringView Answer on Stackoverflow
Solution 5 - RspecacconradView Answer on Stackoverflow
Solution 6 - RspecNickView Answer on Stackoverflow