Comment affirmer le nombre d'éléments en utilisant Capybara avec un message d'erreur approprié?

86

Je sais qu'à Capybara, vous pouvez faire quelque chose comme ceci:

page.should have_css("ol li", :count => 2)

Cependant, en supposant que cette page n'a par exemple qu'un seul élément correspondant, l'erreur n'est pas très descriptive:

  1) initial page load shows greetings
 Failure/Error: page.should have_css("ol li", :count => 2)
 expected css "ol li" to return something

Au lieu de ce message d'erreur plutôt obscur, existe-t-il un moyen d'écrire l'assertion de telle sorte que la sortie d'erreur soit quelque chose comme «Lors de la mise en correspondance de« ol li », attendu: 2, trouvé: 1». De toute évidence, je pourrais créer moi-même une logique personnalisée pour un tel comportement - je demande s'il existe un moyen de le faire «hors de la boîte»?

Pour ce que ça vaut, j'utilise le pilote Selenium et RSpec.

joyeux
la source
Juste pour que les gens sachent, "page.should have_css (" ol li ",: count => 2)" a été implémenté dans capybara. Je pense que c'est très utilisable avec les portées: within ("ol.users-list") do page.should have_css ('li',: count => 3) end
rafaelkin
@rafaelkin, juste pour clarifier: capybara rapporte-t-il maintenant, par exemple, l'inadéquation du nombre d'éléments avec plus de détails? Je n'ai pas suivi capybara depuis un certain temps maintenant, mais le problème à l'époque, lorsque j'ai posé la question, concernait le format du message d'erreur, ce qui page.should have_css("ol li", :count => 2)n'aurait pas déjà été implémenté.
merryprankster
mes amis, j'ai le sentiment que la réponse actuellement acceptée (= la mienne) n'est plus la meilleure, mais je n'ai pas le temps (ne travaillez plus avec Ruby) pour évaluer laquelle des solutions suggérées est la meilleure. Je changerai la réponse acceptée par celle de Richard juste parce qu'elle inclut la sortie de l'assertion qui résout le problème d'origine.
merryprankster

Réponses:

22

Eh bien, comme il semble qu'il n'y ait pas de support prêt à l'emploi, j'ai écrit ce matcher personnalisé:

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

Maintenant, vous pouvez faire des choses comme:

describe "initial page load", :type => :request do
    it "has 12 inputs" do
        visit "/"
        page.should match_exactly(12, "input")
    end
end

et obtenez une sortie comme:

  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

Cela fait l'affaire pour l'instant, je vais chercher à faire cette partie de Capybara.

joyeux
la source
On dirait que résoudre ce problème
merryprankster
14

Je pense que ce qui suit est plus simple, donne une sortie assez claire et élimine le besoin d'un matcher personnalisé.

page.all("ol li").count.should eql(2)

Cela s'imprime ensuite en cas d'erreur:

      expected: 2
       got: 3

  (compared using eql?)
  (RSpec::Expectations::ExpectationNotMetError)
Richard
la source
9
Cela n'attend pas que l'attente se réalise, par exemple lorsqu'il y a encore des requêtes ajax en attente.
Clemens Helm
9

Edit: Comme indiqué par @ThomasWalpole, l'utilisation de alldésactive l'attente / réessayer de Capybara, donc la réponse ci-dessus par @pandaPower est bien meilleure.

Que dis-tu de ça?

  within('ol') do
    expect( all('.opportunity_title_wrap').count ).to eq(2)
  end
Meiring constant
la source
2
Cela empêche complètement les Capybaras d'attendre / réessayer et ne devrait jamais être une solution recommandée.
Thomas Walpole
@ThomasWalpole Je ne sais pas de quoi vous parlez. En quoi la recherche d'un élément dans un autre élément touche-t-elle d'une manière ou d'une autre l'attente / la nouvelle tentative dans Capybara?
Constant Meiring
2
@ConstantMeiring Ce n'est pas le within, il appelle .countles résultats allqui désactive l'attente / réessayer. En appelant countles résultats de all(pour lesquels un "tableau" vide est un retour valide) vous convertissez en un entier et comparez-le. Si cette comparaison échoue, l'attente échoue. Si à la place vous passez l'option count à l'un des correspondants de Capybara, capybara attendra / réessayera de trouver le sélecteur spécifié jusqu'à ce que l'option count corresponde (ou Capybara.default_max_wait_time expire).
Thomas Walpole
4

La meilleure pratique actuelle (02/09/2013) recommandée par Capybara est la suivante ( source ):

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

Acconrad
la source
-4

La réponse de @pandaPower est très bonne, mais la syntaxe était légèrement différente pour moi:

expect(page).to have_selector('.views-row', :count => 30)
pseudo
la source
5
L'utilisation de fusées de hachage ne constitue pas une «syntaxe différente».
premjg
2
Je ne suis pas un développeur ruby ​​et je ne savais pas que les deux syntaxes étaient équivalentes. TBH Je ne suis pas sûr que cela justifie un vote négatif. C'est une alternative valable. Pour ceux qui ne viennent pas d'un fond Ruby, cela peut ne pas sembler évident. Ce n'était pas pour moi.
Nick le