カピバラあいまいさの解決


97

カピバラのあいまいさを解決するにはどうすればよいですか?何らかの理由で、ページ内に同じ値のリンクが必要ですが、エラーが発生したため、テストを作成できません

Failure/Error: click_link("#tag1")
     Capybara::Ambiguous:
       Ambiguous match, found 2 elements matching link "#tag1"

これが避けられないのは、デザインのせいです。右側にツイート/タグがあり、ページの左側にタグがあるTwitterページを再作成しようとしています。したがって、同じリンクページが同じページに表示されることは避けられません。


コードも投稿していただけますか?
Heena Hussain、2012年

8
ページ上の2つの要素に同じIDを割り当てないでください。同じリンクを使用する場合は、要素にIDを割り当てないで、代わりにクラスを使用してください。
Chris Salzberg、2012年

回答:


147

私の解決策は

first(:link, link).click

の代わりに

click_link(link)

6
これについては、カピバラアップグレードガイドで詳しく説明しています。この問題が発生した場合に役立ちます。
リッチー2013年

1
Capybara 2.0の時点では、どうしても必要な場合を除いて、これを行わないでください。以下の@Andreyの回答と、上記にリンクされているアップグレードガイドのあいまい一致の説明を参照してください。
ジム、2014年

4
具体的には、Capybara 2.0にはインテリジェントな待機ロジックがあり、処理速度の異なるマシン間でスペックが一貫して成功または失敗するようにし、必要最小限の時間だけ待機します。first上記のように使用すると、何をしているのか完全に理解していない限り、仕様は合格しますが、CIビルドまたは同僚のマシンでは失敗する可能性があります。
ジム、2014年

1
良い議論については、robots.thoughtbot.com

74

カピバラのそのような行動は意図的なものであり、他のほとんどの回答で示唆されているように修正すべきではないと私は信じています。

2.0より前のバージョンのCapybaraは例外を発生させる代わりに最初の要素を返しましたが、後のCapybaraのメンテナーはそれは悪い考えであり、それを発生させる方がよいと判断しました。多くの状況で、最初の要素を返すと、開発者が返すことを望んでいた要素ではなく戻ることにつながると判断されました。

ここで最も支持されている答えは、first以下を使用することをお勧めします:allfind

  1. allそしてfirstかかわらず、このようなロケータを持つ要素は、ページ上に表示されますまで待機しないfind待機を行います
  2. all(...).firstそしてfirst、将来そのようなロケーターを持つ別の要素がページに表示され、その結果、誤った要素を見つける可能性がある状況からあなたを保護しません

そのため、あいまいさの少ない別のロケーターを選択することをお勧めします。たとえば、ID、クラス、または他のcss / xpathロケーターで要素を選択して、1つの要素のみが一致するようにします。


ここに注記として、あいまいさを解決するときに私が通常役立つと考えるいくつかのロケーターがあります。

  • find('ul > li:first-child')

    first('ul > li')最初liにページに表示されるまで待機するよりも便利です。

  • click_link('Create Account', match: :first)

    first(:link, 'Create Account').click少なくとも1つの[アカウントの作成]リンクがページに表示されるまで待機するよりも優れています。ただし、ページに2回表示されない一意のロケーターを選択する方が良いと思います。

  • fill_in('Password', with: 'secret', exact: true)

    exact: true Capybaraに完全一致のみを検索するように指示します。つまり、「パスワードの確認」を検索しません


7
これが一番の答えになるはずです。カピバラの組み込み待機機能を利用するセレクターを常に使用するようにしてください。
tgf 2014年

ありがとう。:firstを使用しようとしましたが、jQueryでのみ機能することに気付きました。私が探していたのは:first-child
Overload119


24

新しい回答:

あなたは次のようなことを試すことができます

all('a').select {|elt| elt.text == "#tag1" }.first.click

これを行う方法があり、利用可能なカピバラ構文をより有効に利用all("a[text='#tag1']").first.clickできる可能性があります。とはいえ、最初は少し奇妙な状況です。<a>同じでタグをidclassおよびテキスト。find withinDOMの適切なセグメントを実行できるため、それらが異なるdivの子である可能性はありますか?(それはあなたのHTMLソースの少しを見るのに役立ちます)。


古い答え:(「#tag1」は要素idに「tag1」があることを意味すると私が思った場所)

どのリンクをクリックしますか?最初の場合(またはそれが問題ではない場合)は、

find('#tag1').click

それ以外の場合はできます

all('#tag1')[1].click

2番目をクリックします。


最初の解決策はうまくいくかもしれませんが、問題はCSS IDと間違っている可能性があるということです---------失敗/エラー:find( '#tag1')。click#or all( '# tag1 ')[0] .click Capybara :: ElementNotFound:css "#tag1"
neilmarion

find('#tag1')idを持つ要素を1つだけ検索することを意味しますtag1tag1ページにidのある要素がいくつかあるため例外が発生します
Andrei Botalov '19

できますall(:xpath, '//a[text()="#tag1"]').first.click
香川周平

9

あなたはあなたが最初のものを見つけることを確実にすることができますmatch

find('.selector', match: :first).click

ただし、重要なのは、おそらくこれを実行したくないことです。これは、重複出力コードの臭いを無視する脆弱なテストにつながり、次に、一致するものを1つ削除したために、失敗したはずのときに機能し続ける検知につながるためです。要素ですが、テストは他の要素を喜んで見つけました。

より良い賭けは使用することwithinです:

within('#sidebar') do
  find('.selector).click
end

これにより、カピバラの自動待機および自動再試行機能(を使用すると失われるfind('.selector').click)を活用しながら、期待どおりの要素を確実に見つけることができます。また、意図がより明確になります。


7

ここで既存の知識体系に追加するには:

JSテストでは、Capybaraは2つのスレッド(1つはRSpec、もう1つはRails)と2番目のプロセス(ブラウザー)を同期させておく必要があります。これは、ほとんどのマッチャーおよびノー​​ド検索メソッドで(構成された最大待機時間まで)待機することによって行われます。

カピバラには、主に待機しないメソッドもありますNode#all。それらを使用することは、断続的に失敗するように仕様に指示するようなものです。

受け入れられた答えは示唆していpage.first('selector')ます。をNode#first使用してNode#allいるため、これは少なくともJS仕様にとっては望ましくありません。

そうは言っても、カピバラを次のように設定した場合Node#first 待機します。

# rails_helper.rb
Capybara.wait_on_first_by_default = true

このオプションはCapybara 2.5.0で追加され、デフォルトではfalseになっています。

Andreiが述べたように、代わりに

find('selector', match: :first)

または、セレクターを変更します。どちらも、設定やドライバーに関係なくうまく機能します。

さらに複雑なことに、Capybaraの古いバージョン(または設定オプションが有効)では、#findあいまいさを無視して、最初に一致するセレクターを返すだけです。これも仕様が明確ではなくなるため、あまり良くありません。おそらくデフォルトの動作ではなくなったのではないでしょうか。詳細については既に説明済みなので、省略します。

その他のリソース:



2

上記のすべてのオプションを考慮すると、これも試すことができます

find("a", text: text, match: :prefer_exact).click

きゅうりを使っているならこれもフォローできます

テキストをシナリオステップからパラメーターとして渡すことができます。これは、再利用するための一般的なステップになる場合があります。

何かのようなもの When a user clicks on "text" link

そしてステップ定義で When(/^(?:user) clicks on "([^"]*)" (?:link)$/) do |text|

このように、コードの行を最小限に抑えることで同じ手順を再利用でき、新しいキュウリのシナリオを簡単に作成できます


0

きゅうりのあいまいなエラーを避けるため。

解決策1

first("#tag1").click

解決策2

Cucumber features/filename.feature --guess
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.