Wie löse ich Mehrdeutigkeiten in Capybara auf? Aus irgendeinem Grund benötige ich Links mit denselben Werten auf einer Seite, kann jedoch keinen Test erstellen, da der Fehler angezeigt wird
Failure/Error: click_link("#tag1")
Capybara::Ambiguous:
Ambiguous match, found 2 elements matching link "#tag1"
Der Grund, warum ich dies nicht vermeiden kann, ist das Design. Ich versuche, die Twitter-Seite mit Tweets / Tags rechts und den Tags links auf der Seite neu zu erstellen. Daher ist es unvermeidlich, dass identische Links auf derselben Seite angezeigt werden.
ruby-on-rails-3
rspec
capybara
neilmarion
quelle
quelle
Antworten:
Meine Lösung ist
anstatt
quelle
first
wie oben vorgeschlagen führt, sofern Sie nicht genau wissen, was Sie tun, wahrscheinlich zu Spezifikationen, die für Sie gelten, aber in einem CI-Build oder auf dem Computer eines Kollegen fehlschlagen.Ein solches Verhalten von Capybara ist beabsichtigt und ich glaube, es sollte nicht behoben werden, wie in den meisten anderen Antworten vorgeschlagen.
Versionen von Capybara vor 2.0 gaben das erste Element zurück, anstatt eine Ausnahme auszulösen, aber spätere Betreuer von Capybara entschieden, dass es eine schlechte Idee ist und es besser ist, sie auszulösen. Es wurde entschieden, dass in vielen Situationen die Rückgabe des ersten Elements dazu führt, dass nicht das Element zurückgegeben wird, das der Entwickler zurückgeben wollte.
Die am besten bewertete Antwort hier empfiehlt die Verwendung von
first
oderall
anstelle vonfind
aber:all
undfirst
warten Sie nicht, bis ein Element mit einem solchen Locator auf der Seite angezeigtfind
wirdall(...).first
undfirst
schützt Sie nicht vor der Situation, dass in Zukunft ein anderes Element mit einem solchen Locator auf der Seite angezeigt wird und Sie möglicherweise ein falsches Element findenSo es eine andere wählen adviced ist, weniger zweideutig Locator : zum Beispiel wählt Element von ID, Klasse oder anderen css / XPath - Locator , so dass nur ein Element es übereinstimmen.
Als Hinweis hier einige Locators, die ich normalerweise als nützlich erachte, um Mehrdeutigkeiten aufzulösen:
find('ul > li:first-child')
Es ist nützlicher als
first('ul > li')
zu warten, bis das ersteli
Mal auf der Seite erscheint.click_link('Create Account', match: :first)
Es ist besser als
first(:link, 'Create Account').click
zu warten, bis mindestens ein Link zum Erstellen eines Kontos auf der Seite angezeigt wird. Ich glaube jedoch, dass es besser ist, einen eindeutigen Locator zu wählen, der nicht zweimal auf der Seite erscheint.fill_in('Password', with: 'secret', exact: true)
exact: true
fordert Capybara auf, nur genaue Übereinstimmungen zu finden, dh keine "Passwortbestätigung" zu findenquelle
Die obige Lösung funktioniert hervorragend, aber für Neugierige können Sie auch die folgende Syntax verwenden.
Weitere Informationen finden Sie hier:
http://taimoorchangaizpucitian.wordpress.com/2013/09/06/capybara-click-link-different-cases-and-solutions/
quelle
NEUE ANTWORT:
Sie können so etwas versuchen
Möglicherweise gibt es eine Möglichkeit, die verfügbare Capybara-Syntax besser zu nutzen - etwas in der Art von,
all("a[text='#tag1']").first.click
aber ich kann mir die richtige Syntax nicht sofort vorstellen und finde keine geeignete Dokumentation. Das heißt, es ist zunächst eine etwas seltsame Situation, zwei<a>
Tags mit demselben zuid
haben.class
und Text. Gibt es eine Chance, dass sie Kinder verschiedener Divs sind, da Sie dannfind
within
das entsprechende Segment des DOM erstellen können? (Es wäre hilfreich, ein bisschen von Ihrer HTML-Quelle zu sehen).ALTE ANTWORT: (wo ich dachte, '# tag1' bedeutete, dass das Element ein
id
"tag1" hatte)Auf welchen der Links möchten Sie klicken? Wenn es das erste ist (oder es keine Rolle spielt), können Sie es tun
Sonst kannst du machen
um auf den zweiten zu klicken.
quelle
find('#tag1')
bedeutet, dass Sie nur ein Element mit der ID finden möchtentag1
. Ausnahme wirdtag1
all(:xpath, '//a[text()="#tag1"]').first.click
.Sie können sicherstellen, dass Sie den ersten finden, indem Sie
match
:Aber wichtig ist, dass Sie dies wahrscheinlich nicht möchten , da dies zu spröden Tests führt , bei denen der Geruch von Code mit doppelter Ausgabe ignoriert wird, was wiederum zu Fehlalarmen führt, die weiter funktionieren, wenn sie hätten fehlschlagen sollen, weil Sie eine Übereinstimmung entfernt haben Element, aber der Test fand glücklich den anderen.
Die bessere Wette ist zu verwenden
within
:Dies stellt sicher, dass Sie das Element finden, das Sie erwarten, und nutzt gleichzeitig die automatischen Warte- und Wiederholungsfunktionen von Capybara (die Sie verlieren, wenn Sie es verwenden
find('.selector').click
), und macht deutlich, was die Absicht ist.quelle
Um das vorhandene Wissen hier zu erweitern:
Für JS-Tests muss Capybara zwei Threads (einen für RSpec, einen für Rails) und einen zweiten Prozess (den Browser) synchron halten. Dies geschieht durch Warten (bis zur konfigurierten maximalen Wartezeit) in den meisten Matchern und Knotenfindungsmethoden.
Capybara hat auch Methoden, die in erster Linie nicht warten
Node#all
. Wenn Sie sie verwenden, teilen Sie Ihren Spezifikationen mit, dass sie zeitweise fehlschlagen sollen.Die akzeptierte Antwort schlägt vor
page.first('selector')
. Dies ist zumindest für JS-Spezifikationen wegen der Verwendung unerwünschtNode#first
Node#all
.Das heißt,
Node#first
ich werde warten, wenn Sie Capybara so konfigurieren:Diese Option wurde in Capybara 2.5.0 hinzugefügt und ist standardmäßig falsch.
Wie Andrei erwähnt, sollten Sie stattdessen verwenden
oder ändern Sie Ihren Selektor. Entweder funktioniert gut, unabhängig von Konfiguration oder Treiber.
Um die Sache noch weiter zu verkomplizieren, werden in alten Versionen von Capybara (oder mit aktivierter Konfigurationsoption)
#find
Mehrdeutigkeiten gerne ignoriert und nur der erste passende Selektor zurückgegeben. Dies ist auch nicht besonders gut, da dadurch Ihre Spezifikationen weniger explizit werden. Ich kann mir vorstellen, dass dies nicht mehr das Standardverhalten ist. Ich werde die Einzelheiten weglassen, da sie oben bereits besprochen wurden.Mehr Ressourcen:
quelle
Aufgrund dieses Beitrags können Sie ihn über die Option "Übereinstimmung" beheben:
quelle
Wenn Sie alle oben genannten Optionen in Betracht ziehen, können Sie dies auch versuchen
Wenn Sie Gurke verwenden, können Sie dies auch befolgen
Sie können den Text als Parameter aus den Szenarioschritten übergeben, die ein allgemeiner Schritt zur erneuten Verwendung sein können
Etwas wie
When a user clicks on "text" link
Und in Schritt Definition
When(/^(?:user) clicks on "([^"]*)" (?:link)$/) do |text|
Auf diese Weise können Sie denselben Schritt wiederverwenden, indem Sie die Codezeilen minimieren, und es wäre einfach, neue Gurkenszenarien zu schreiben
quelle
Um mehrdeutige Gurkenfehler zu vermeiden.
Lösung 1
Lösung 2
quelle