ChatGPT解决这个技术问题 Extra ChatGPT

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

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.

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
@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.
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.

p
pandaPowder

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


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

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.


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

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)

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

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

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).
a
acconrad

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

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


N
Nick

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

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

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.

关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now