Ich arbeite mit dem Selenium WebDriver 2.25.0 an einer mehrsprachigen Webanwendung und teste hauptsächlich den Seiteninhalt (für verschiedene Sprachen wie Arabisch, Englisch, Russisch usw.).
Für meine Anwendung ist die Leistung je nach Leistung besser. Stellen Sie sicher, dass alle Browser unterstützt werden (d. H. IE 7,8,9, FF, Chrome usw.).
Vielen Dank im Voraus für Ihre wertvollen Vorschläge.
CSS-Selektoren sind wesentlich besser als Xpath und in der Selenium-Community gut dokumentiert. Hier sind einige Gründe,
Es gibt jedoch Situationen, in denen Sie xpath verwenden müssen, z. B. nach einem übergeordneten Element suchen oder nach seinem Text suchen (ich würde das später nicht empfehlen).
Sie können den Blog von Simon hier lesen. Er empfiehlt auch CSS über Xpath.
Wenn Sie Inhalte testen, verwenden Sie keine Selektoren, die vom Inhalt der Elemente abhängig sind. Das ist ein Alptraum der Wartung für jedes Gebiet. Versuchen Sie, mit Entwicklern zu sprechen, und wenden Sie Techniken an, mit denen der Text in der Anwendung ausgelagert wurde, z. B. Wörterbücher oder Ressourcenpakete usw. Hier ist mein blog , der ihn ausführlich erklärt.
Dank @parishodak ist hier der link , der die Zahlen angibt, die zeigen, dass die CSS-Leistung besser ist
Ich werde den unpopulären Selenium-Tag für SO halten, dass XPATH ist vorzuziehen längerfristig für CSS.
Lassen Sie mich zuerst "den Elefanten im Raum" in Angriff nehmen - xpath ist langsamer als css.
Mit der aktuellen CPU-Leistung (gelesen: alles, was in den letzten 5 Jahren hergestellt wurde), sogar auf VMs mit Browserstack/Saucelabs, und der Entwicklung der Browser (gelesen: alle gängigen in den letzten 5 Jahren) ist das kaum der Fall. Die Browser-Engines haben sich entwickelt, die Unterstützung von xpath ist einheitlich, IE ist nicht im Bild (hoffentlich für die meisten von uns). Dieser Vergleich in der anderen Antwort wird überall zitiert, ist aber sehr kontextabhängig - wie viele laufen gegen IE8 oder interessieren sich dafür?
Wenn es einen Unterschied gibt, handelt es sich um einen Bruchteil einer Millisekunde.
Die meisten übergeordneten Frameworks fügen jedoch mindestens 1ms Overhead über den rohen Selen-Aufruf hinzu (Wrapper, Handler, Speichern von Bundesstaaten usw.); Meine persönliche Waffe - Robotframework - fügt mindestens 2ms hinzu, was ich mehr als gerne opfern werde für das, was es bietet. Ein Netzwerk-Roundtrip von einer AWS us-east-1 zum BrowserStack-Hub lautet normalerweise 11 Millisekunden.
Wenn also bei Remote-Browsern ein Unterschied zwischen xpath und css besteht, wird dies durch alles andere überschattet.
Es gibt nicht so viele öffentliche Vergleiche (ich habe den zitierten wirklich gesehen), also - hier ist ein grober Einzelfall, ein Dummy und ein einfacher Fall. Das Ziel - die Zielseite von BrowserStack und die Schaltfläche "Anmelden". ein Screenshot der HTML:
Hier ist der Testcode (Python):
from Selenium import webdriver
import timeit
if __== '__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 mit Python nicht 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 - für xpath - "ein div-Element mit genau diesem Klassenwert irgendwo im DOM"; Das CSS ist ähnlich - "ein Div-Element mit dieser Klasse irgendwo im DOM". Bewusst gewählt, nicht überabgestimmt zu sein; Außerdem wird der Klassenselektor für das CSS als "das zweitschnellste nach einer ID" bezeichnet.
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 regelmäßiges i7-Tier).
Hier ist die Ausgabe:
css Gesamtzeit 1000 Wiederholungen: 8.84s, pro Fund: 8.84ms
xpath Gesamtzeit für 1000 Wiederholungen: 8.52s, pro Fund: 8.52ms
Offensichtlich sind die Zeitangaben für die Suche ziemlich nah. der Unterschied ist 0. 32 Millisekunden. Springen Sie nicht - der Xpath ist schneller - manchmal ist er es, manchmal ist es css.
Versuchen wir es mit einem anderen Satz von Locators, ein bisschen komplizierter - ein Attribut mit einer Unterzeichenfolge (zumindest für mich ist es üblich, die Klasse eines Elements zu verfolgen, wenn ein Teil davon funktionale Bedeutung hat):
xpath_locator = '//div[contains(@class, "button-section")]'
css_locator = 'div[class~=button-section]'
Die beiden Locators sind wiederum semantisch gleich - "Finden Sie ein div-Element, das in seiner Klasse diesen Teilstring enthält" . Hier sind die Ergebnisse:
css Gesamtzeit 1000 Wiederholungen: 8.60s, pro Fund: 8.60ms
xpath Gesamtzeit für 1000 Wiederholungen: 8.75s, pro Fund: 8.75ms
Diff von 0,15 ms.
Bei einem komplexeren DOM sind die Ergebnisse gleich. Ich habe keine öffentlich verfügbaren URLs zur Verfügung, um ein Beispiel zu geben, aber ich habe wieder ähnliche Ergebnisse für xpath und css gesehen.
Als Übung - derselbe Test wie im verknüpften Blog in den Kommentaren/anderen Antworten - die Testseite ist öffentlich, und so ist der Testing Code .
Sie tun ein paar Dinge im Code: Klicken Sie auf eine Spalte, um sie zu sortieren, holen Sie sich die Werte und überprüfen Sie die Sortierung der Benutzeroberfläche. Ich werde es abschneiden - hol einfach die Locators, nach all dem ist der Wurzeltest, richtig?
Derselbe Code wie oben, mit folgenden Änderungen:
Die URL ist jetzt http://the-internet.herokuapp.com/tables
; Es gibt 2 Tests.
Die Locators für den ersten - "Finding Elements By ID and Class" - sind:
css_locator = '#table2 tbody .dues'
xpath_locator = "//table[@id='table2']//tr/td[contains(@class,'dues')]"
Das Ergebnis:
css Gesamtzeit 1000 Wiederholungen: 8,24 Sekunden, pro Fund: 8,24 ms
xpath Gesamtzeit für 1000 Wiederholungen: 8.45s, pro Fund: 8.45ms
Diff von 0,2 Millisekunden.
Die "Elemente finden beim Durchqueren":
css_locator = '#table1 tbody tr td:nth-of-type(4)'
xpath_locator = "//table[@id='table1']//tr/td[4]"
Das Ergebnis:
css Gesamtzeit 1000 Wiederholungen: 9.29s, pro Fund: 9.29ms
xpath Gesamtzeit für 1000 Wiederholungen: 8.79s, pro Fund: 8.79ms
Diesmal sind es 0,5 ms (hier ist xpath "schneller").
Welches der beiden Programme sollte aus Xpath und CSS ausgewählt werden? Die Antwort ist einfach - wählen Sie Suchen nach ID.
Um es kurz zu machen: Wenn die ID eines Elements eindeutig ist (wie es den Spezifikationen entspricht), spielt dessen Wert eine wichtige Rolle in der internen Darstellung des DOM durch den Browser und ist daher normalerweise die schnellste.
Warum halte ich xpath für eine bessere Leistung? Einfach - Vielseitigkeit und Kraft.
Xpath ist eine Sprache, die für die Arbeit mit XML-Dokumenten entwickelt wurde. Als solches erlaubt es wesentlich leistungsfähigere Konstrukte als css.
Zum Schluss noch einmal sehr subjektiv - welches ist zu wählen? IMHO gibt es keine richtige oder falsche Wahl - es gibt unterschiedliche Lösungen für das gleiche Problem, und was für den Job besser geeignet ist, sollte ausgewählt werden. Als Fan von xpath bin ich nicht schüchtern, um in meinen Projekten eine Mischung aus beidem zu verwenden - manchmal ist es viel schneller, einfach eine CSS zu werfen, wenn ich weiß, dass es die Arbeit gut machen wird.
Finally, again very subjective - which one to chose? IMHO, there is no right or wrong choice - they are different solutions to the same problem, and whatever is more suitable for the job should be picked. Being fan of xpath I'm not shy to use in my projects a mix of both - heck, sometimes it is much faster to just throw a css one, if I know it will do the work just fine.
Die Debatte zwischen cssSelector vs XPath würde als eine der subjektivsten Debatten in der Selen-Community bleiben . Was wir bisher bereits wissen, lässt sich wie folgt zusammenfassen:
Dave Haeffner hat einen Test auf a durchgeführt Seite mit zwei HTML-Datentabellen , eine Tabelle ist ohne hilfreiche Attribute geschrieben ( [~ # ~] id [~ # ~] und Klasse ) und die andere mit ihnen. Ich habe die Testprozedur und das Ergebnis dieses Experiments im Detail in der Diskussion analysiert Warum sollte ich jemals cssSelector verwenden? ) Selektoren im Gegensatz zu XPath für automatisierte Tests? . Während dieses Experiment zeigte, dass jedes Locator Strategy für alle Browser einigermaßen gleich ist, hat es nicht das ganze Bild für uns angemessen gezeichnet. Dave Haeffner in der anderen Diskussion Css Vs. X Path, Under a Microscope erwähnt, in einem End-to-End-Test waren viele andere Variablen im Spiel Soßenstart , Starten des Browsers und Latenz zu und von der zu testenden Anwendung. Der unglückliche Ausweg aus diesem Experiment könnte sein, dass ein Fahrer schneller ist als der andere (zB [~ # ~] dh [~ # ~] vs Firefox , obwohl das überhaupt nicht der Fall war. Um einen wirklichen Eindruck davon zu bekommen, wie hoch der Leistungsunterschied zwischen cssSelector und XPath ist, mussten wir tiefer gehen. Wir haben das getan, indem wir alles von einem lokalen Computer aus ausgeführt haben, während wir ein Leistungsbenchmarking-Dienstprogramm verwendet haben. Wir haben uns auch eher auf eine bestimmte Selen-Aktion als auf den gesamten Testlauf konzentriert und die Dinge mehrmals ausgeführt. Ich habe das spezifische Testverfahren und das Ergebnis dieses Experiments ausführlich in der Diskussion analysiert cssSelector vs XPath for Selenium . Bei den Tests fehlte jedoch immer noch ein Aspekt, d. H. mehr Browserabdeckung (z. B. Internet Explorer 9 und 10) und Testen gegen eine größere und tiefere Seite.
Dave Haeffner in einer anderen Diskussion Css Vs. X Path, Under a Microscope (Part 2) erwähnt, um sicherzustellen, dass die erforderlichen Benchmarks bestmöglich abgedeckt sind, müssen wir Folgendes berücksichtigen: ein Beispiel, das eine große und tiefe Seite zeigt .
Zur Veranschaulichung dieses detaillierten Beispiels wurde eine Windows XP= virtuelle Maschine eingerichtet und Ruby (1.9.3) installiert. Alle verfügbaren Browser und die entsprechenden Browsertreiber für Selenium Für das Benchmarking wurde Rubys Standardlib benchmark
verwendet.
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
[~ # ~] note [~ # ~] : Die Ausgabe erfolgt in Sekunden und die Ergebnisse beziehen sich auf die Gesamtlaufzeit von 100 Ausführungen.
In Tabellenform:
In Diagrammform:
Sie können das Benchmarking selbst durchführen, indem Sie diese Bibliothek verwenden, wobei Dave Haeffner den gesamten Code zusammenfasst.