CSS-Selektoren sind weitaus leistungsfähiger als Xpath und in der Selenium-Community gut dokumentiert. Hier sind einige Gründe,
- Xpath-Engines sind in jedem Browser unterschiedlich und daher inkonsistent
- IE verfügt nicht über eine native xpath-Engine, daher injiziert Selen eine eigene xpath-Engine, um die Kompatibilität seiner API zu gewährleisten. Daher verlieren wir den Vorteil, native Browserfunktionen zu verwenden, die WebDriver von Natur aus fördert.
- Xpath neigen dazu, komplex zu werden und daher meiner Meinung nach schwer zu lesen
Es gibt jedoch Situationen, in denen Sie xpath verwenden müssen, z. B. um nach einem übergeordneten Element oder einem Suchelement anhand seines Textes zu suchen (das würde ich später nicht empfehlen).
Sie können Blog von Simon lesen hier . Er empfiehlt auch CSS über Xpath.
Wenn Sie Inhalte testen, verwenden Sie keine Selektoren, die vom Inhalt der Elemente abhängig sind. Das wird ein Alptraum für jedes Gebietsschema. Versuchen Sie, mit Entwicklern zu sprechen, und verwenden Sie Techniken, mit denen sie den Text in der Anwendung ausgelagert haben, z. B. Wörterbücher oder Ressourcenpakete usw. Hier ist mein Blog , in dem dies ausführlich erläutert wird.
bearbeiten 1
Dank @parishodak ist hier der Link, der die Zahlen enthält, die beweisen, dass die CSS-Leistung besser ist
Ich werde die unpopuläre Meinung zu SO-Selen-Tags vertreten, dass XPath auf längere Sicht CSS vorzuziehen ist .
Dieser lange Beitrag besteht aus zwei Abschnitten: Zuerst lege ich einen Beweis für die Rückseite der Serviette vor, dass der Leistungsunterschied zwischen den beiden 0,1 bis 0,3 Millisekunden beträgt (ja, das sind 100 Mikrosekunden ) , und dann teile ich meine Meinung dazu mit XPath ist mächtiger.
Leistungsunterschied
Lassen Sie uns zuerst "den Elefanten im Raum" angehen - dieser xpath ist langsamer als css.
Mit der aktuellen CPU-Leistung (lesen Sie: alles, was x86 seit 2013 produziert) , selbst auf Browserstack- / Saucelabs / aws-VMs, und der Entwicklung der Browser (lesen Sie: alle gängigen in den letzten 5 Jahren) ist dies kaum der Fall. Die Engines des Browsers haben sich entwickelt, die Unterstützung von xpath ist einheitlich, IE ist nicht im Bilde (hoffentlich für die meisten von uns) . Dieser Vergleich in der anderen Antwort wird überall zitiert, ist aber sehr kontextbezogen - wie viele führen die Automatisierung gegen IE8 aus oder kümmern sich darum?
Wenn es einen Unterschied gibt, ist es in einem Bruchteil einer Millisekunde .
Die meisten übergeordneten Frameworks verursachen jedoch ohnehin mindestens 1 ms Overhead gegenüber dem Roh-Selen-Aufruf (Wrapper, Handler, State Storing usw.). Meine persönliche Waffe der Wahl - RobotFramework - fügt mindestens 2 ms hinzu, was ich gerne für das opfere, was sie bietet. Eine Netzwerkrundfahrt von einem AWS us-east-1 zum BrowserStack-Hub dauert normalerweise 11 Millisekunden .
Wenn also bei Remote-Browsern ein Unterschied zwischen xpath und css besteht, wird dieser von allem anderen in Größenordnungen überschattet.
Die Messungen
Es gibt nicht so viele öffentliche Vergleiche (ich habe wirklich nur den zitierten gesehen) , also - hier ist ein grober Einzelfall, Dummy und einfacher.
Es wird ein Element anhand der beiden Strategien X-mal lokalisieren und die durchschnittliche Zeit dafür vergleichen.
Das Ziel - die Zielseite von BrowserStack und die Schaltfläche "Anmelden"; Ein Screenshot des HTML-Codes beim Schreiben dieses Beitrags:
Hier ist der Testcode (Python):
from selenium import webdriver import timeit if __name__ == '__main__': xpath_locator = '//div[@class="button-section col-xs-12 row"]' css_locator = 'div.button-section.col-xs-12.row' repetitions = 1000 driver = webdriver.Chrome() driver.get('https://www.browserstack.com/') css_time = timeit.timeit("driver.find_element_by_css_selector(css_locator)", number=repetitions, globals=globals()) xpath_time = timeit.timeit('driver.find_element_by_xpath(xpath_locator)', number=repetitions, globals=globals()) driver.quit() print("css total time {} repeats: {:.2f}s, per find: {:.2f}ms". format(repetitions, css_time, (css_time/repetitions)*1000)) print("xpath total time for {} repeats: {:.2f}s, per find: {:.2f}ms". format(repetitions, xpath_time, (xpath_time/repetitions)*1000))
Für diejenigen, die nicht mit Python vertraut sind - es öffnet die Seite und findet das Element - zuerst mit dem CSS-Locator, dann mit dem xpath; Die Suchoperation wird 1.000 Mal wiederholt. Die Ausgabe ist die Gesamtzeit in Sekunden für die 1.000 Wiederholungen und die durchschnittliche Zeit für einen Fund in Millisekunden.
Die Locators sind:
Absichtlich gewählt, um nicht überstimmt zu werden; Außerdem wird der Klassenselektor für das CSS als "der zweitschnellste nach einer ID" angegeben.
Die Umgebung - Chrome v66.0.3359.139, chromedriver v2.38, CPU: ULV Core M-5Y10 läuft normalerweise mit 1,5 GHz (ja, eine "Textverarbeitung", nicht einmal ein normales i7-Biest) .
Hier ist die Ausgabe:
Offensichtlich sind die Zeitpunkte pro Fund ziemlich nahe beieinander. Die Differenz beträgt 0,32 Millisekunden . Springe nicht "der xpath ist schneller" - manchmal ist es, manchmal ist es CSS.
Versuchen wir es mit einem anderen Satz von Locatoren, der etwas komplizierter ist - einem Attribut mit einer Teilzeichenfolge (zumindest für mich ein gängiger Ansatz, der der Klasse eines Elements nachgeht, wenn ein Teil davon eine funktionale Bedeutung hat) :
xpath_locator = '//div[contains(@class, "button-section")]' css_locator = 'div[class~=button-section]'
Die beiden Locators sind wieder semantisch gleich - "finde ein div-Element, dessen Klassenzeichenfolge in dieser Klasse liegt".
Hier sind die Ergebnisse:
Diff von 0,15 ms .
Als Übung - der gleiche Test wie im verlinkten Blog in den Kommentaren / anderen Antworten - ist die Testseite öffentlich, ebenso wie der Testcode .
Sie machen ein paar Dinge im Code - klicken auf eine Spalte, um danach zu sortieren, dann die Werte abzurufen und zu überprüfen, ob die Sortierung der Benutzeroberfläche korrekt ist.
Ich werde es schneiden - besorgen Sie sich doch nur die Locators - das ist der Wurzeltest, oder?
Der gleiche Code wie oben, mit folgenden Änderungen in:
Die URL ist jetzt
http://the-internet.herokuapp.com/tables
; Es gibt 2 Tests.Die Locators für den ersten - "Finden von Elementen nach ID und Klasse" - sind:
css_locator = '#table2 tbody .dues' xpath_locator = "//table[@id='table2']//tr/td[contains(@class,'dues')]"
Und hier ist das Ergebnis:
Diff von 0,2 Millisekunden.
Die "Elemente durch Überqueren finden":
css_locator = '#table1 tbody tr td:nth-of-type(4)' xpath_locator = "//table[@id='table1']//tr/td[4]"
Das Ergebnis:
Diesmal sind es 0,5 ms (umgekehrt ist xpath hier "schneller" geworden).
Also 5 Jahre später (bessere Browser-Engines) und nur auf die Leistung des Locators fokussiert (keine Aktionen wie Sortieren in der Benutzeroberfläche usw.), dasselbe Testfeld - es gibt praktisch keinen Unterschied zwischen CSS und XPath.
Also, aus xpath und css, welche der beiden für die Leistung wählen? Die Antwort ist einfach - wählen Sie die Suche nach ID .
Kurz gesagt, wenn die ID eines Elements eindeutig ist (wie es den Spezifikationen entsprechen soll), spielt sein Wert eine wichtige Rolle bei der internen Darstellung des DOM durch den Browser und ist daher normalerweise die schnellste.
Es sind jedoch nicht immer eindeutige und konstante (z. B. nicht automatisch generierte) IDs verfügbar. Dies führt uns zu "Warum XPath, wenn es CSS gibt?".
Der XPath-Vorteil
Warum finde ich xpath besser, wenn die Leistung nicht im Bild ist? Einfach - Vielseitigkeit und Kraft.
Xpath ist eine Sprache, die für die Arbeit mit XML-Dokumenten entwickelt wurde. als solches erlaubt es viel leistungsfähigere Konstrukte als CSS.
Zum Beispiel Navigation in alle Richtungen im Baum - finden Sie ein Element, gehen Sie dann zu seinem Großelternteil und suchen Sie nach einem Kind mit bestimmten Eigenschaften.
Es erlaubt eingebettete boolesche Bedingungen -
cond1 and not(cond2 or not(cond3 and cond4))
; eingebettete Selektoren - "Finden Sie ein Div mit diesen untergeordneten Elementen mit diesen Attributen und navigieren Sie danach".XPath ermöglicht die Suche basierend auf dem Wert eines Knotens (seinem Text) - obwohl diese Vorgehensweise verpönt ist, ist es besonders in schlecht strukturierten Dokumenten nützlich (keine bestimmten Attribute, auf die man treten kann, wie dynamische IDs und Klassen) Inhalt) .
Das Einsteigen in CSS ist definitiv einfacher - man kann in wenigen Minuten mit dem Schreiben von Selektoren beginnen; Aber nach ein paar Tagen der Nutzung hat die Leistungsfähigkeit und die Möglichkeiten von xpath CSS schnell überwunden.
Und rein subjektiv - ein komplexes CSS ist viel schwerer zu lesen als ein komplexer xpath-Ausdruck.
Outro;)
Zum Schluss nochmal sehr subjektiv - welches soll man wählen?
IMO, es gibt keine richtige oder falsche Wahl - es handelt sich um unterschiedliche Lösungen für dasselbe Problem, und es sollte ausgewählt werden, was für den Job besser geeignet ist.
Als "Fan" von XPath bin ich nicht schüchtern, in meinen Projekten eine Mischung aus beidem zu verwenden - zum Teufel, manchmal ist es viel schneller, nur ein CSS zu werfen, wenn ich weiß, dass es die Arbeit gut macht.
quelle
[]
nach//
zum Beispiel) . Aber nach dem ersten Tag des Lernens und der Verwendung überschreitet so ziemlich jeder den Wendepunkt der Lernkurve :) (CSS-Einstieg ist zugegebenermaßen einfacher, IMHO) .Die Debatte zwischen cssSelector und XPath würde als eine der subjektivsten Debatten in der Selenium Community bleiben . Was wir bisher bereits wissen, lässt sich wie folgt zusammenfassen:
Dave Haeffner führte einen Test auf einer Seite mit zwei HTML-Datentabellen durch , wobei eine Tabelle ohne hilfreiche Attribute ( ID und Klasse ) und die andere mit diesen geschrieben wurde. Ich habe das Testverfahren und das Ergebnis dieses Experiments in der Diskussion detailliert analysiert. Warum sollte ich jemals cssSelector- Selektoren im Gegensatz zu XPath für automatisierte Tests verwenden? . Während dieses Experiment zeigte, dass jede Locator-Strategie für alle Browser einigermaßen gleichwertig ist, hat es nicht das gesamte Bild für uns angemessen gezeichnet. Dave Haeffner in der anderen Diskussion Css Vs. X-Pfad unter einem Mikroskoperwähnt, in einem End-to-End - Test gab es eine Menge anderer Variablen im Spiel Start Sauce , Browser starten , und Latenz zu und von der zu testenden Anwendung. Die unglückliche Erkenntnis aus diesem Experiment könnte sein, dass ein Fahrer möglicherweise schneller ist als der andere (z. B. IE gegen Firefox ), obwohl dies überhaupt nicht der Fall war. Um einen echten Eindruck von dem Leistungsunterschied zwischen cssSelector und XPath zu bekommenWir mussten tiefer graben. Dazu haben wir alles von einem lokalen Computer aus ausgeführt und dabei ein Leistungsbenchmarking-Dienstprogramm verwendet. Wir haben uns auch auf eine bestimmte Selen-Aktion konzentriert und nicht auf den gesamten Testlauf. Ich habe das spezifische Testverfahren und das Ergebnis dieses Experiments in der Diskussion cssSelector vs XPath für Selen detailliert analysiert . Bei den Tests fehlte jedoch noch ein Aspekt, nämlich eine größere Browserabdeckung (z. B. Internet Explorer 9 und 10) und das Testen anhand einer größeren und tieferen Seite.
Dave Haeffner in einer anderen Diskussion Css Vs. X Path, Under a Microscope (Teil 2) erwähnt, um sicherzustellen, dass die erforderlichen Benchmarks bestmöglich abgedeckt werden, müssen wir ein Beispiel betrachten, das eine große und tiefe Seite demonstriert .
Versuchsaufbau
Um dieses detaillierte Beispiel zu demonstrieren, wurde eine virtuelle Windows XP-Maschine eingerichtet und Ruby (1.9.3) installiert. Alle verfügbaren Browser und die entsprechenden Browsertreiber für Selenium wurden ebenfalls installiert. Für das Benchmarking wurde Rubys Standardbibliothek
benchmark
verwendet.Testcode
require_relative 'base' require 'benchmark' class LargeDOM < Base LOCATORS = { nested_sibling_traversal: { css: "div#siblings > div:nth-of-type(1) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3)", xpath: "//div[@id='siblings']/div[1]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]" }, nested_sibling_traversal_by_class: { css: "div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1", xpath: "//div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]" }, table_header_id_and_class: { css: "table#large-table thead .column-50", xpath: "//table[@id='large-table']//thead//*[@class='column-50']" }, table_header_id_class_and_direct_desc: { css: "table#large-table > thead .column-50", xpath: "//table[@id='large-table']/thead//*[@class='column-50']" }, table_header_traversing: { css: "table#large-table thead tr th:nth-of-type(50)", xpath: "//table[@id='large-table']//thead//tr//th[50]" }, table_header_traversing_and_direct_desc: { css: "table#large-table > thead > tr > th:nth-of-type(50)", xpath: "//table[@id='large-table']/thead/tr/th[50]" }, table_cell_id_and_class: { css: "table#large-table tbody .column-50", xpath: "//table[@id='large-table']//tbody//*[@class='column-50']" }, table_cell_id_class_and_direct_desc: { css: "table#large-table > tbody .column-50", xpath: "//table[@id='large-table']/tbody//*[@class='column-50']" }, table_cell_traversing: { css: "table#large-table tbody tr td:nth-of-type(50)", xpath: "//table[@id='large-table']//tbody//tr//td[50]" }, table_cell_traversing_and_direct_desc: { css: "table#large-table > tbody > tr > td:nth-of-type(50)", xpath: "//table[@id='large-table']/tbody/tr/td[50]" } } attr_reader :driver def initialize(driver) @driver = driver visit '/large' is_displayed?(id: 'siblings') super end # The benchmarking approach was borrowed from # http://rubylearning.com/blog/2013/06/19/how-do-i-benchmark-ruby-code/ def benchmark Benchmark.bmbm(27) do |bm| LOCATORS.each do |example, data| data.each do |strategy, locator| bm.report(example.to_s + " using " + strategy.to_s) do begin ENV['iterations'].to_i.times do |count| find(strategy => locator) end rescue Selenium::WebDriver::Error::NoSuchElementError => error puts "( 0.0 )" end end end end end end end
Ergebnisse
In Tabellenform:
In Diagrammform:
Analyse der Ergebnisse
Zusammenfassung
Wissenswertes
Mit dieser Bibliothek, in der Dave Haeffner den gesamten Code verpackt hat, können Sie das Benchmarking selbst durchführen .
quelle