102 votes

Quelle est la différence entre cssSelector et Xpath et lequel est le meilleur en termes de performances pour les tests inter-filières ?

Je travaille avec Selenium WebDriver 2.25.0 sur une application Web multilingue et je teste principalement le contenu des pages (pour différentes langues comme l'arabe, l'anglais, le russe, etc.)

Pour mon application, quelle est la meilleure solution en termes de performances ? Assurez-vous qu'elle soit compatible avec tous les navigateurs (IE 7, 8, 9, FF, Chrome, etc.).

Merci d'avance pour vos précieuses suggestions.

121voto

nilesh Points 3914

Les sélecteurs CSS sont bien plus performants que Xpath et ils sont bien documentés dans la communauté Selenium. Voici quelques raisons,

  • Les moteurs Xpath sont différents dans chaque navigateur, ce qui les rend incohérents.
  • IE ne dispose pas d'un moteur xpath natif, c'est pourquoi Selenium injecte son propre moteur xpath pour la compatibilité de son API. Nous perdons donc l'avantage d'utiliser les fonctionnalités natives du navigateur que WebDriver favorise par nature.
  • Xpath a tendance à devenir complexe et donc difficile à lire à mon avis.

Cependant, dans certaines situations, il est nécessaire d'utiliser xpath, par exemple pour rechercher un élément parent ou un élément par son texte (je ne recommande pas cette dernière option).

Vous pouvez lire le blog de Simon aquí . Il recommande également CSS plutôt que Xpath.

Si vous testez le contenu, n'utilisez pas de sélecteurs qui dépendent du contenu des éléments. Ce serait un cauchemar de maintenance pour chaque locale. Essayez de parler avec les développeurs et utilisez les techniques qu'ils ont utilisées pour externaliser le texte dans l'application, comme les dictionnaires ou les paquets de ressources, etc. Voici mon blog qui l'explique en détail.

édition 1

Grâce à @parishodak, voici la lien qui fournit les chiffres prouvant que les performances des CSS sont meilleures

65voto

Todor Points 5347

Je vais garder l'opinion impopulaire sur le tag SO selenium que XPath est préférable à la CSS à long terme.

Ce long billet se divise en deux parties : d'abord, je vais prouver, à l'aide d'une méthode approximative, que la différence de performance entre les deux est de 1,5 million d'euros. 0,1-0,3 millisecondes (oui, c'est 100 micro secondes) Puis je vous dirai pourquoi XPath est plus puissant.


Différence de performance

Commençons par aborder "l'éléphant dans la pièce", à savoir que xpath est plus lent que css.

Avec la puissance actuelle des processeurs (lire : tout x86 produit depuis 2013) même sur des VMs browserstack/saucelabs/aws, et le développement des navigateurs (lire : tous les films populaires des 5 dernières années) c'est loin d'être le cas. Les moteurs des navigateurs ont évolué, le support de xpath est uniforme, IE n'est plus dans le coup (espérons-le pour la plupart d'entre nous) . Cette comparaison dans l'autre réponse est citée un peu partout, mais elle est très contextuelle - combien de personnes exécutent - ou se soucient - de l'automatisation contre IE8 ?

S'il y a une différence, c'est dans une fraction de milliseconde .

Pourtant, la plupart des frameworks de niveau supérieur ajoutent au moins 1 ms de surcharge par rapport à l'appel brut de selenium (wrappers, handlers, stockage d'état, etc.) ; mon arme personnelle de choix - RobotFramework - ajoute au moins 2 ms, que je suis plus qu'heureux de sacrifier pour ce qu'elle fournit. Un aller-retour réseau d'un AWS us-east-1 au hub de BrowserStack est généralement de 11 millisecondes .

Ainsi, avec les navigateurs distants, s'il existe une différence entre xpath et css, elle est éclipsée par tout le reste, en ordres de grandeur.


Les mesures

Il n'y a pas beaucoup de comparaisons publiques (Je n'ai vraiment vu que celui qui est cité) Voici donc un cas unique, factice et simple.
Il va localiser un élément par les deux stratégies X fois, et comparer le temps moyen pour cela.

La cible - la page d'accueil de BrowserStack, et son bouton "Sign Up" ; une capture d'écran du code html au moment de la rédaction de cet article :

enter image description here

Voici le code de test (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))

Pour ceux qui ne sont pas familiers avec Python - il ouvre la page, et trouve l'élément - d'abord avec le localisateur css, puis avec le xpath ; l'opération de recherche est répétée 1.000 fois. La sortie est le temps total en secondes pour les 1 000 répétitions, et le temps moyen pour une recherche en millisecondes.

Les localisateurs sont :

  • pour xpath - "un élément div ayant cette valeur de classe exacte, quelque part dans le DOM" ;
  • le css est similaire - "un élément div avec cette classe, quelque part dans le DOM".

Délibérément choisi pour ne pas être surajouté ; aussi, le sélecteur de classe est cité pour le css comme "le deuxième plus rapide après un id".

L'environnement - Chrome v66.0.3359.139, chromedriver v2.38, cpu : ULV Core M-5Y10 fonctionnant habituellement à 1.5GHz (oui, un "traitement de texte", même pas une bête i7 normale) .

Voici le résultat :

css total time 1000 repeats: 8.84s, per find: 8.84ms

xpath total time for 1000 repeats: 8.52s, per find: 8.52ms

De toute évidence, les temps de recherche sont assez proches ; la différence est la suivante 0.32 millisecondes . Ne sautez pas sur "le xpath est plus rapide" - parfois c'est le cas, parfois c'est le css.


Essayons avec un autre ensemble de localisateurs, un peu plus compliqué - un attribut ayant une sous-chaîne (approche courante, du moins pour moi, qui consiste à s'en prendre à la classe d'un élément lorsqu'une partie de celui-ci a une signification fonctionnelle). :

xpath_locator = '//div[contains(@class, "button-section")]'
css_locator = 'div[class~=button-section]'

Les deux localisateurs sont sémantiquement identiques - "trouver un élément div ayant dans son attribut class cette sous-chaîne".
Voici les résultats :

css total time 1000 repeats: 8.60s, per find: 8.60ms

xpath total time for 1000 repeats: 8.75s, per find: 8.75ms

Diff de 0.15ms .


À titre d'exercice, le même test que celui effectué dans le cadre de l'enquête sur la santé des femmes. blog lié dans les commentaires/autre réponse - le page de test est public, tout comme le code de test .

Ils font plusieurs choses dans le code - cliquer sur une colonne pour la trier, puis obtenir les valeurs, et vérifier que le tri de l'interface utilisateur est correct.
Je vais le couper - il suffit de prendre les localisateurs, après tout - c'est le test Root, non ?

Le même code que ci-dessus, avec ces modifications :

  • L'url est maintenant http://the-internet.herokuapp.com/tables ; il y a 2 tests.

  • Les localisateurs pour le premier - "Finding Elements By ID and Class" - sont les suivants :

    css_locator = '#table2 tbody .dues' xpath_locator = "//table[@id='table2']//tr/td[contains(@class,'dues')]"

Et voici le résultat :

css total time 1000 repeats: 8.24s, per find: 8.24ms

xpath total time for 1000 repeats: 8.45s, per find: 8.45ms

Diff de 0.2 millisecondes.

La "recherche d'éléments par parcours" :

css_locator = '#table1 tbody tr td:nth-of-type(4)'
xpath_locator = "//table[@id='table1']//tr/td[4]"

Le résultat :

css total time 1000 repeats: 9.29s, per find: 9.29ms

xpath total time for 1000 repeats: 8.79s, per find: 8.79ms

Cette fois, c'est 0.5 ms (en sens inverse, xpath s'est avéré plus "rapide" ici).

Donc 5 ans plus tard (meilleurs moteurs de navigation) et Si l'on se concentre uniquement sur les performances des localisateurs (pas d'actions comme le tri dans l'interface utilisateur, etc.), avec le même banc d'essai, il n'y a pratiquement aucune différence entre CSS et XPath.


Donc, entre xpath et css, lequel des deux choisir pour les performances ? La réponse est simple : choisissez localisation par identification .

Pour faire court, si l'identifiant d'un élément est unique (comme il est censé l'être selon les spécifications), sa valeur joue un rôle important dans la représentation interne du DOM par le navigateur, et est donc généralement la plus rapide.

Pourtant, unique et constant (par exemple, non généré automatiquement) Les identifiants ne sont pas toujours disponibles, ce qui nous amène à la question "pourquoi XPath s'il y a CSS ?".


L'avantage de XPath

Sans parler des performances, pourquoi je pense que xpath est meilleur ? C'est simple : la polyvalence et la puissance.

Xpath est un langage développé pour travailler avec des documents XML ; en tant que tel, il permet des constructions beaucoup plus puissantes que css.
Par exemple, la navigation dans toutes les directions de l'arbre - trouver un élément, puis aller à son grand-parent et chercher un enfant de celui-ci ayant certaines propriétés.
Il permet d'intégrer des conditions booléennes - cond1 and not(cond2 or not(cond3 and cond4)) ; sélecteurs intégrés - "trouver un div ayant ces enfants avec ces attributs, puis naviguer en fonction de celui-ci".
XPath permet d'effectuer des recherches sur la base de la valeur d'un nœud (son texte) - même si cette pratique est mal vue, elle s'avère utile, notamment dans les documents mal structurés. (pas d'attributs définis sur lesquels s'appuyer, comme les ids et les classes dynamiques - localisez l'élément par son contenu textuel) .

Le démarrage en css est définitivement plus facile - on peut commencer à écrire des sélecteurs en quelques minutes ; mais après quelques jours d'utilisation, la puissance et les possibilités offertes par xpath l'emportent rapidement sur css.
Et purement subjectif - un css complexe est beaucoup plus difficile à lire qu'une expression xpath complexe.

Outro ;)

Enfin, encore une fois très subjectif, lequel choisir ?

Il n'y a pas de bon ou de mauvais choix - ce sont des solutions différentes au même problème, et il faut choisir celle qui convient le mieux à la situation.

Étant un "fan" de XPath, je n'hésite pas à utiliser dans mes projets un mélange des deux. Parfois, il est beaucoup plus rapide de lancer un CSS, si je sais qu'il fera parfaitement l'affaire.

19voto

New contributor Points 31198

Le débat entre cssSelector vs XPath resterait l'un des débats les plus subjectifs de l'histoire de l'humanité. Communauté Selenium . Ce que nous savons déjà jusqu'à présent peut être résumé comme suit :

  • Les personnes en faveur de cssSelector disent qu'il est plus lisible et plus rapide (surtout lorsqu'il est exécuté contre Internet Explorer).
  • Alors que ceux qui sont en faveur de XPath vanter sa capacité à traverser la page (tout en cssSelector ne peut pas).
  • Traverser le DOM dans des navigateurs plus anciens comme IE8 ne fonctionne pas avec cssSelector mais il n'y a pas de problème avec XPath .
  • XPath peut remonter le DOM (par exemple de l'enfant au parent), alors que cssSelector ne peut parcourir le DOM que vers le bas (par exemple, de parent à enfant).
  • Cependant, le fait de ne pas pouvoir parcourir le DOM avec cssSelector dans les anciens navigateurs n'est pas nécessairement une mauvaise chose, car il s'agit plutôt d'un indicateur que votre page est mal conçue et qu'elle pourrait bénéficier d'un balisage utile.
  • Ben Burton mentionne que vous devez utiliser cssSelector parce que c'est comme ça que les applications sont construites. Cela rend les tests plus faciles à écrire, à discuter et à faire maintenir par d'autres.
  • Adam Goucher dit d'adopter une approche plus hybride - en se concentrant d'abord sur les ID, puis sur les cssSelector et en tirant parti de XPath seulement quand vous en avez besoin (par exemple, en remontant le DOM) et que XPath sera toujours plus puissant pour les localisateurs avancés.

Dave Haeffner a effectué un test sur une page avec deux tableaux de données HTML une table est écrite sans attributs utiles ( ID y Classe ), et l'autre avec eux. J'ai analysé les procédure de test et le résultat de cette expérience en détail dans la discussion Pourquoi devrais-je utiliser cssSelector les sélecteurs par rapport à XPath pour les tests automatisés ? . Bien que cette expérience ait démontré que chaque Stratégie de localisation est raisonnablement équivalente d'un navigateur à l'autre, elle ne nous a pas permis de dresser un tableau complet de la situation. Dave Haeffner dans l'autre discussion Css Vs. X Path, sous un microscope Dans un test de bout en bout, il y a beaucoup d'autres variables en jeu. Démarrage de la sauce , Démarrage du navigateur y latence vers et depuis l'application testée. Le résultat malheureux de cette expérience pourrait être qu'un pilote peut être plus rapide que l'autre (par ex. IE vs Firefox ), alors qu'en fait, ce n'était pas du tout le cas. Pour avoir un aperçu réel de la différence de performance entre cssSelector y XPath nous avons dû creuser plus profondément. Nous l'avons fait en exécutant tout depuis une machine locale tout en utilisant un utilitaire d'évaluation des performances. Nous nous sommes également concentrés sur une action Selenium spécifique plutôt que sur l'ensemble du test, et nous avons exécuté les choses plusieurs fois. J'ai analysé l'action spécifique procédure de test et le résultat de cette expérience en détail dans la discussion cssSelector vs XPath pour selenium . Mais il manquait toujours un aspect aux tests, à savoir une couverture plus étendue des navigateurs (par exemple, Internet Explorer 9 et 10) et des tests sur une page plus grande et plus profonde.

Dave Haeffner dans une autre discussion Css Vs. X Path, sous un microscope (partie 2) Afin de s'assurer que les repères requis sont couverts de la meilleure façon possible, nous devons prendre en compte les éléments suivants un exemple qui démontre une page large et profonde .


Test SetUp

Pour illustrer cet exemple détaillé, une machine virtuelle Windows XP a été configurée et Ruby (1.9.3) a été installé. Tous les navigateurs disponibles et leurs pilotes équivalents pour Selenium ont également été installés. Pour le benchmarking, la librairie standard de Ruby benchmark a été utilisé.


Code d'essai

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

Résultats

NOTE : La sortie est en secondes, et les résultats sont pour la durée totale de 100 exécutions.

Sous forme de tableau :

css_xpath_under_microscopev2

Sous forme de tableau :

  • Chrome :

chart-chrome

  • Firefox :

chart-firefox

  • Internet Explorer 8 :

chart-ie8

  • Internet Explorer 9 :

chart-ie9

  • Internet Explorer 10 :

chart-ie10

  • Opéra :

chart-opera


Analyse des résultats

  • Chrome et Firefox sont clairement réglés pour être plus rapides cssSelector performance.
  • Internet Explorer 8 est un mélange de cssSelector qui ne fonctionnera pas, une société hors de contrôle XPath qui prend ~65 secondes, et une traversée de table de 38 secondes sans aucune cssSelector résultat pour le comparer.
  • Dans IE 9 et 10, XPath est globalement plus rapide. Dans Safari, c'est du pareil au même, à l'exception de quelques traversées plus lentes avec l'application XPath . Et sur presque tous les navigateurs, la traversée de la fratrie imbriquée y traversée des cellules du tableau fait avec XPath sont une opération coûteuse.
  • Cela ne devrait pas être si surprenant puisque les localisateurs sont fragiles et inefficaces et que nous devons les éviter.

Résumé

  • Dans l'ensemble, il y a deux circonstances où XPath est nettement plus lent que cssSelector . Mais ils sont facilement évitables.
  • La différence de performance est légèrement en faveur de Sélecteurs css pour les navigateurs non-IE et légèrement en faveur de xpath pour les navigateurs IE.

Trivia

Vous pouvez effectuer le bench-marking vous-même, en utilisant cette bibliothèque donde Dave Haeffner a emballé tout le code.

0voto

Sriram Points 91

Le principal avantage de l'utilisation de CSS dans IE (généralement dans la plupart des navigateurs) est qu'il est un peu plus rapide que xpath. Vous pouvez consulter l'URL ci-dessous pour avoir une meilleure idée.

CSS et xpath

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X