100

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.

3
  • Just to people know, "page.should have_css("ol li", :count => 2)" was implemented in capybara. I think it is highly usable with scopes: within("ol.users-list") do page.should have_css('li', :count => 3) end Commented Apr 18, 2013 at 19:47
  • @rafaelkin, just to clarify: does capybara now report e.g. the mismatch in element count with more detail? I haven't followed capybara for a while now, but the issue back then when I made the question was about the format of error message, not that page.should have_css("ol li", :count => 2) would not have been implemented already. Commented Apr 22, 2013 at 12:24
  • folks, I have a feeling that currently accepted answer (=my own) is no longer the best, but do not have time (no longer work with Ruby) to evaluate which of the suggested solutions is the best. I'll change the accepted answer to that of Richard's just because it includes the output of assertion which addresses the original issue. Commented Oct 28, 2013 at 7:32

6 Answers 6

198

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

Sign up to request clarification or add additional context in comments.

1 Comment

Also works with have_css: expect(page).to have_css('input', count: 12)
22

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.

1 Comment

Looks like fixing this in Capybara is not straightforward: github.com/jnicklas/capybara/issues/331
16

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) 

1 Comment

This doesn't wait for the expectation to come true, e.g. when there are still pending ajax requests.
10

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 

3 Comments

This completely defeats Capybaras waiting/retrying and should never be a recommended solution.
@ThomasWalpole I'm not sure what you're talking about. How does looking for an element within another element in any way touch the waiting/retrying in Capybara?
@ConstantMeiring It's not the 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).
6

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

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

Comments

-5

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

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

2 Comments

Using hash rockets does not qualify as "different syntax."
I'm not a ruby dev and didn't realise that the two syntaxes were equivalent. TBH I'm not sure it warrants downvoting. It's a valid alternative. For those not from a Ruby background it may not seem obvious. It wasn't for me.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.