wake-up-neo.com

Was ist der Unterschied zwischen css Selector und Xpath und was ist besser für die Leistung beim Cross-Browser-Testen?

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.

53
Chetan

CSS-Selektoren sind wesentlich besser als Xpath und in der Selenium-Community gut dokumentiert. Hier sind einige Gründe,

  • Xpath-Engines unterscheiden sich in jedem Browser und sind daher inkonsistent
  • IE verfügt nicht über eine native Xpath-Engine, daher injiziert Selenium seine eigene Xpath-Engine, um die API zu unterstützen. Daher verlieren wir den Vorteil der Verwendung nativer Browserfunktionen, die WebDriver von Natur aus fördert.
  • Xpath neigt 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. 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.

edit 1

Dank @parishodak ist hier der link , der die Zahlen angibt, die zeigen, dass die CSS-Leistung besser ist

69
nilesh

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:

 enter image description here

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.

12
Todor Minakov

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:

  • Leute, die sich für cssSelector aussprechen, sagen, dass es besser lesbar und schneller ist (besonders wenn es gegen den Internet Explorer läuft).
  • Während diejenigen, die sich für XPath aussprechen, die Möglichkeit haben, die Seite zu durchlaufen (während cssSelector dies nicht kann).
  • Das Durchlaufen des DOM in älteren Browsern wie IE8 funktioniert nicht mit cssSelector , ist aber mit XPath in Ordnung.
  • XPath kann das DOM durchlaufen (z. B. von Kind zu Eltern), wohingegen cssSelector das DOM nur durchlaufen kann (z. B. von Eltern) zum Kind)
  • Es ist jedoch nicht unbedingt eine schlechte Sache, das DOM mit cssSelector in älteren Browsern nicht durchlaufen zu können, da dies eher ein Anzeichen dafür ist, dass Ihre Seite ein schlechtes Design hat und von einigen profitieren könnte hilfreiche Auszeichnung.
  • Ben Burton erwähnt, dass Sie cssSelector verwenden sollten, da auf diese Weise Anwendungen erstellt werden. Auf diese Weise können die Tests leichter geschrieben, besprochen und von anderen unterstützt werden.
  • Adam Goucher befürwortet einen eher hybriden Ansatz - zuerst die IDs, dann cssSelector und XPath nur, wenn Sie es brauchen (z. B. das DOM hochgehen) und das XPath wird für fortgeschrittene Locators immer leistungsfähiger sein.

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 .


Versuchsaufbau

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.


Code testen

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

[~ # ~] note [~ # ~] : Die Ausgabe erfolgt in Sekunden und die Ergebnisse beziehen sich auf die Gesamtlaufzeit von 100 Ausführungen.

In Tabellenform:

css_xpath_under_microscopev2

In Diagrammform:

  • Chrome :

chart-chrome

  • Firefox :

chart-firefox

  • Internet Explorer 8 :

chart-ie8

  • Internet Explorer 9 :

chart-ie9

  • Internet Explorer 10 :

chart-ie10

  • Opera :

chart-opera


Analyse der Ergebnisse

  • Chrome und Firefox sind klar aufeinander abgestimmt, um eine schnellere cssSelector -Leistung zu erzielen.
  • Internet Explorer 8 ist eine Sammlung von cssSelector , die nicht funktioniert, eine außer Kontrolle geratene XPath -Überquerung, die ~ 65 Sekunden dauert und eine 38-Sekunden-Tabellendurchquerung ohne cssSelector zum Vergleich.
  • In IE 9 und 10, XPath ist insgesamt schneller. In Safari ist es ein Fehler, mit Ausnahme einiger langsamerer Überquerungsläufe mit XPath . Und in fast allen Browsern Traversal der verschachtelten Geschwister und Traversal der Tabellenzellen erledigt mit XPath sind eine teure Operation.
  • Dies sollte nicht so überraschend sein, da die Lokalisierer spröde und ineffizient sind und wir sie vermeiden müssen.

Zusammenfassung

  • Insgesamt gibt es zwei Umstände, in denen XPath deutlich langsamer ist als cssSelector . Sie sind aber leicht zu vermeiden.
  • Der Leistungsunterschied ist leicht zugunsten von css-selectors für Nicht-IE-Browser und leicht zugunsten von xpath für IE Browser.

Trivia

Sie können das Benchmarking selbst durchführen, indem Sie diese Bibliothek verwenden, wobei Dave Haeffner den gesamten Code zusammenfasst.

7
DebanjanB