Les sélecteurs CSS fonctionnent bien mieux que Xpath et il est bien documenté dans la communauté Selenium. Voici quelques raisons,
- Les moteurs Xpath sont différents dans chaque navigateur, ce qui les rend incohérents
- IE n'a pas de moteur xpath natif, donc le sélénium injecte son propre moteur xpath pour la compatibilité de son API. Par conséquent, nous perdons l'avantage d'utiliser des fonctionnalités de navigateur natives que WebDriver promeut par nature.
- Xpath a tendance à devenir complexe et donc difficile à lire à mon avis
Cependant, dans certaines situations, vous devez utiliser xpath, par exemple, rechercher un élément parent ou rechercher un élément par son texte (je ne recommanderais pas ce dernier).
Vous pouvez lire le blog de Simon ici . Il recommande également CSS sur Xpath.
Si vous testez du contenu, n'utilisez pas de sélecteurs qui dépendent du contenu des éléments. Ce sera un cauchemar de maintenance pour chaque région. Essayez de parler avec les développeurs et utilisez des techniques qu'ils ont utilisées pour externaliser le texte dans l'application, comme des dictionnaires ou des ensembles de ressources, etc. Voici mon blog qui l'explique en détail.
modifier 1
Grâce à @parishodak, voici le lien qui fournit les chiffres prouvant que les performances CSS sont meilleures
Je vais tenir l'opinion impopulaire sur la balise SO sélénium selon laquelle XPath est préférable à CSS à long terme.
Ce long article comporte deux sections - d'abord, je vais mettre une preuve à l'arrière de la serviette, la différence de performance entre les deux est de 0,1 à 0,3 millisecondes (oui; c'est 100 microsecondes ) , puis je partagerai mon opinion pourquoi XPath est plus puissant.
Différence de performance
Commençons par aborder "l'éléphant dans la pièce" - ce xpath est plus lent que le CSS.
Avec la puissance actuelle du processeur (lire: tout ce qui a été produit par x86 depuis 2013) , même sur les VM Browserstack / saucelabs / aws, et le développement des navigateurs (lire: tous les plus populaires au cours des 5 dernières années), ce n'est guère le cas. Les moteurs du navigateur se sont développés, le support de xpath est uniforme, IE est hors de propos (espérons-le pour la plupart d'entre nous) . Cette comparaison dans l'autre réponse est citée partout, mais elle est très contextuelle - combien exécutent - ou se soucient - de l'automatisation par rapport à IE8?
S'il y a une différence, c'est en une fraction de milliseconde .
Pourtant, la plupart des frameworks de niveau supérieur ajoutent au moins 1 ms de surcharge par rapport à l'appel de sélénium brut de toute façon (wrappers, gestionnaires, 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 entre un AWS us-east-1 et le hub de BrowserStack dure généralement 11 millisecondes .
Donc, avec les navigateurs distants, s'il y a une différence entre xpath et css, elle est éclipsée par tout le reste, par ordre de grandeur.
Les mesures
Il n'y a pas beaucoup de comparaisons publiques (je n'ai vraiment vu que celle citée) , donc - voici un cas unique, factice et simple.
Il localisera un élément par les deux stratégies X fois et comparera le temps moyen pour cela.
La cible - la page de destination de BrowserStack, et son bouton «Inscription»; une capture d'écran du html en écrivant ce post:
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 découverte en millisecondes.
Les localisateurs sont:
Choisi délibérément pour ne pas être sur-accordé; aussi, le sélecteur de classe est cité pour le css comme "le deuxième plus rapide après un identifiant".
L'environnement - Chrome v66.0.3359.139, chromedriver v2.38, processeur: ULV Core M-5Y10 fonctionnant généralement à 1,5 GHz (oui, un "traitement de texte", pas même une bête i7 ordinaire) .
Voici le résultat:
De toute évidence, les délais de recherche sont assez proches; la différence est de 0,32 millisecondes . Ne sautez pas "le xpath est plus rapide" - parfois c'est le cas, parfois c'est css.
Essayons avec un autre ensemble de localisateurs, un tout petit peu plus compliqué - un attribut ayant une sous-chaîne (approche commune au moins pour moi, aller après la classe d'un élément quand 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 à nouveau sémantiquement les mêmes - "trouver un élément div ayant dans son attribut de classe cette sous-chaîne".
Voici les résultats:
Diff de 0,15 ms .
En tant qu'exercice - le même test que celui effectué dans le blog lié dans les commentaires / autre réponse - la page de test est publique, tout comme le code de test .
Ils font quelques choses dans le code - en cliquant sur une colonne pour trier par elle, puis en obtenant les valeurs et en vérifiant que le tri de l'interface utilisateur est correct.
Je vais le couper - il suffit de récupérer les localisateurs, après tout - c'est le test racine, non?
Le même code que ci-dessus, avec ces modifications dans:
L'URL est maintenant
http://the-internet.herokuapp.com/tables
; il y a 2 tests.Les localisateurs du premier - "Recherche d'éléments par ID et par classe" - sont:
css_locator = '#table2 tbody .dues' xpath_locator = "//table[@id='table2']//tr/td[contains(@class,'dues')]"
Et voici le résultat:
Diff de 0,2 millisecondes.
"Recherche d'éléments en parcourant":
css_locator = '#table1 tbody tr td:nth-of-type(4)' xpath_locator = "//table[@id='table1']//tr/td[4]"
Le résultat:
Cette fois, il est de 0,5 ms (en sens inverse, xpath s'est avéré "plus rapide" ici).
Donc 5 ans plus tard (meilleurs moteurs de navigateurs) et en se concentrant uniquement sur les performances des localisateurs (pas d'actions comme le tri dans l'interface utilisateur, etc.), le même banc d'essai - il n'y a pratiquement aucune différence entre CSS et XPath.
Alors, hors xpath et css, lequel des deux choisir pour les performances? La réponse est simple: choisissez la localisation par identifiant .
Pour faire court, si l'id d'un élément est unique (comme il est supposé l'être selon les spécifications), sa valeur joue un rôle important dans la représentation interne du navigateur du DOM, et est donc généralement la plus rapide.
Pourtant, les identifiants uniques et constants (par exemple non générés automatiquement) ne sont pas toujours disponibles, ce qui nous amène à "pourquoi XPath s'il y a du CSS?"
L'avantage XPath
Avec les performances hors de l'image, pourquoi est-ce que je pense que XPath est meilleur? Simple - polyvalence et 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'arborescence - trouvez un élément, puis allez chez ses grands-parents et recherchez un enfant de celui-ci ayant certaines propriétés.
Il permet des conditions booléennes intégrées -
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 une recherche basée sur la valeur d'un nœud (son texte) - aussi désapprouvée que cette pratique soit, elle est utile en particulier dans les documents mal structurés (pas d'attributs définis sur lesquels marcher, comme les identifiants et les classes dynamiques - localisez l'élément par son texte contenu) .
Le pas dans le CSS est certainement 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 de xpath ont rapidement dépassé le 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?
OMI, il n'y a pas de bon ou de mauvais choix - ce sont des solutions différentes au même problème, et ce qui convient le mieux au travail doit être choisi.
Étant "fan" de XPath, je ne suis pas gêné d'utiliser dans mes projets un mélange des deux - diable, parfois il est beaucoup plus rapide d'en lancer un CSS, si je sais que cela fera très bien le travail.
la source
[]
après//
par exemple) . Mais après le premier jour d'apprentissage et d'utilisation, à peu près tout le monde franchit le point de basculement de la courbe d'apprentissage :) (l'étape css est admirablement plus facile, à mon humble avis) .Le débat entre cssSelector et XPath resterait l'un des débats les plus subjectifs de la communauté Selenium . Ce que nous savons déjà jusqu'à présent peut se résumer comme suit:
Dave Haeffner a effectué un test sur une page avec deux tableaux de données HTML , un tableau est écrit sans attributs utiles ( ID et Classe ), et l'autre avec eux. J'ai analysé la procédure de test et le résultat de cette expérience en détail dans la discussion Pourquoi devrais-je utiliser les sélecteurs cssSelector par opposition à XPath pour les tests automatisés? . Bien que cette expérience ait démontré que chaque stratégie de localisation est raisonnablement équivalente sur tous les navigateurs, elle ne nous a pas suffisamment brossé le tableau. Dave Haeffner dans l'autre discussion Css Vs. Chemin X, sous un microscopementionné, il y avait beaucoup d'autres variables en jeu dans un un test de bout en bout démarrage de la sauce , le navigateur de démarrage et temps d' attente vers et depuis l'application en cours de test. Le résultat malheureux de cette expérience pourrait être qu'un pilote peut être plus rapide que l'autre (par exemple IE vs Firefox ), alors qu'en fait, ce n'est pas du tout le cas. Pour avoir un aperçu de la différence de performances entre cssSelector et XPath, nous devions creuser plus profondément. Nous l'avons fait en exécutant tout à partir d'une machine locale tout en utilisant un utilitaire d'analyse comparative des performances. Nous nous sommes également concentrés sur une action Selenium spécifique plutôt que sur l'ensemble du test, et avons exécuté les choses plusieurs fois. J'ai analysé en détail la procédure de test spécifique et le résultat de cette expérience dans la discussion cssSelector vs XPath pour le sélénium . Mais les tests manquaient encore un aspect, à savoir une plus grande couverture du navigateur (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, Under a Microscope (Part 2) mentionne, afin de s'assurer que les repères requis sont couverts de la meilleure façon possible, nous devons considérer un exemple qui démontre une page grande 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 de navigateur équivalents pour Selenium ont également été installés. Pour l'analyse comparative, la bibliothèque standard de Ruby a
benchmark
été utilisée.Code de test
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
Sous forme de tableau:
Sous forme de graphique:
Analyse des résultats
Sommaire
Trivia
Vous pouvez effectuer le benchmarking vous-même, en utilisant cette bibliothèque où Dave Haeffner a enveloppé tout le code.
la source