Os seletores CSS têm um desempenho muito melhor do que o Xpath e estão bem documentados na comunidade Selenium. Aqui estão alguns motivos,
- Os motores Xpath são diferentes em cada navegador, portanto, os torna inconsistentes
- O IE não tem um mecanismo xpath nativo, portanto, o selenium injeta seu próprio mecanismo xpath para compatibilidade de sua API. Conseqüentemente, perdemos a vantagem de usar recursos nativos do navegador que o WebDriver promove inerentemente.
- O Xpath tende a se tornar complexo e, portanto, dificultar a leitura na minha opinião
No entanto, existem algumas situações em que você precisa usar o xpath, por exemplo, procurando por um elemento pai ou procurando um elemento por seu texto (eu não recomendaria o último).
Você pode ler o blog de Simon aqui . Ele também recomenda CSS em vez de Xpath.
Se você estiver testando o conteúdo, não use seletores que dependem do conteúdo dos elementos. Isso será um pesadelo de manutenção para todos os locais. Tente conversar com desenvolvedores e usar técnicas que eles usaram para externalizar o texto no aplicativo, como dicionários ou pacotes de recursos etc. Aqui está meu blog que explica isso em detalhes.
editar 1
Graças a @parishodak, aqui está o link que fornece os números que comprovam que o desempenho do CSS é melhor
Vou manter a opinião impopular sobre a tag selênio do SO de que XPath é preferível a CSS a longo prazo.
Esta longa postagem tem duas seções - primeiro vou colocar uma prova de trás do guardanapo de que a diferença de desempenho entre os dois é de 0,1-0,3 milissegundos (sim; são 100 microssegundos ) , e então compartilharei minha opinião por que XPath é mais poderoso.
Diferença de desempenho
Vamos primeiro abordar "o elefante na sala" - esse xpath é mais lento que o css.
Com o poder da CPU atual (leia-se: qualquer coisa x86 produzida desde 2013) , mesmo nas VMs browserstack / saucelabs / aws, e o desenvolvimento dos navegadores (leia-se: todos os populares nos últimos 5 anos) isso dificilmente é o caso. Os motores do navegador foram desenvolvidos, o suporte do xpath é uniforme, o IE está fora de cogitação (espero que para a maioria de nós) . Essa comparação na outra resposta está sendo citada em todos os lugares, mas é muito contextual - quantos estão executando - ou se preocupam com - automação contra o IE8?
Se houver diferença, é em uma fração de milissegundo .
Ainda assim, a maioria das estruturas de nível superior adiciona pelo menos 1 ms de sobrecarga sobre a chamada de selênio bruto de qualquer maneira (wrappers, manipuladores, armazenamento de estado etc); minha arma pessoal de escolha - RobotFramework - adiciona pelo menos 2ms, que estou mais do que feliz em sacrificar pelo que ele oferece. Uma viagem de ida e volta da rede de um AWS us-east-1 para o hub do BrowserStack geralmente dura 11 milissegundos .
Portanto, com navegadores remotos, se houver uma diferença entre xpath e css, ela será ofuscada por todo o resto, em ordens de magnitude.
As medições
Não há muitas comparações públicas (eu realmente vi apenas a citada) , então - aqui está um caso simples, fictício e simples.
Ele localizará um elemento pelas duas estratégias X vezes e comparará o tempo médio para isso.
O destino - a página de destino do BrowserStack e seu botão "Cadastre-se"; uma captura de tela do html ao escrever este post:
Aqui está o código de teste (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))
Para quem não está familiarizado com Python - ele abre a página e encontra o elemento - primeiro com o localizador css, depois com o xpath; a operação de localização é repetida 1.000 vezes. A saída é o tempo total em segundos para as 1.000 repetições e o tempo médio para uma descoberta em milissegundos.
Os localizadores são:
Deliberadamente escolhido para não ser excessivamente ajustado; além disso, o seletor de classe é citado para o css como "o segundo mais rápido depois de um id".
O ambiente - Chrome v66.0.3359.139, chromedriver v2.38, cpu: ULV Core M-5Y10 geralmente rodando a 1,5 GHz (sim, um "processador de texto", nem mesmo um i7 besta regular) .
Aqui está o resultado:
Obviamente, os tempos de busca são bem próximos; a diferença é de 0,32 milissegundos . Não pule "o xpath é mais rápido" - às vezes é, às vezes é css.
Vamos tentar com outro conjunto de localizadores, um pouco mais complicado - um atributo com uma substring (abordagem comum, pelo menos para mim, ir atrás da classe de um elemento quando uma parte dela tem um significado funcional) :
xpath_locator = '//div[contains(@class, "button-section")]' css_locator = 'div[class~=button-section]'
Os dois localizadores são semanticamente iguais - "encontre um elemento div tendo em seu atributo de classe esta substring".
Aqui estão os resultados:
Dif de 0,15ms .
Como um exercício - o mesmo teste feito no blog vinculado nos comentários / outra resposta - a página de teste é pública, assim como o código de teste .
Eles estão fazendo algumas coisas no código - clicando em uma coluna para classificar por ela, depois obtendo os valores e verificando se a classificação da IU está correta.
Vou cortar - basta pegar os localizadores, afinal - esse é o teste de raiz, certo?
O mesmo código acima, com essas alterações em:
O url é agora
http://the-internet.herokuapp.com/tables
; existem 2 testes.Os localizadores para o primeiro - "Finding Elements by ID and Class" - são:
css_locator = '#table2 tbody .dues' xpath_locator = "//table[@id='table2']//tr/td[contains(@class,'dues')]"
E aqui está o resultado:
Diferença de 0,2 milissegundos.
O "Finding Elements By Traversing":
css_locator = '#table1 tbody tr td:nth-of-type(4)' xpath_locator = "//table[@id='table1']//tr/td[4]"
O resultado:
Desta vez, é 0,5 ms (ao contrário, xpath saiu "mais rápido" aqui).
Então, 5 anos depois (motores de navegadores melhores) e com foco apenas no desempenho dos localizadores (sem ações como ordenar na IU, etc), o mesmo testbed - não há praticamente nenhuma diferença entre CSS e XPath.
Então, de xpath e css, qual dos dois escolher para desempenho? A resposta é simples - escolha a localização por id .
Resumindo, se o id de um elemento é único (como deveria ser de acordo com as especificações), seu valor desempenha um papel importante na representação interna do navegador do DOM e, portanto, é geralmente o mais rápido.
No entanto, ids únicos e constantes (por exemplo, não gerados automaticamente) nem sempre estão disponíveis, o que nos leva a "por que XPath se existe CSS?"
A vantagem do XPath
Com o desempenho fora de cogitação, por que acho que o xpath é melhor? Simples - versatilidade e potência.
Xpath é uma linguagem desenvolvida para trabalhar com documentos XML; como tal, ele permite construções muito mais poderosas do que o css.
Por exemplo, navegação em todas as direções da árvore - encontre um elemento, vá até seu avô e procure um filho dele com certas propriedades.
Ele permite condições booleanas embutidas -
cond1 and not(cond2 or not(cond3 and cond4))
; seletores incorporados - "encontre um div que tenha esses filhos com esses atributos e navegue de acordo com ele".XPath permite pesquisar com base no valor de um nó (seu texto) - por mais desaprovado que essa prática seja, ela é útil especialmente em documentos mal estruturados (sem atributos definidos para pisar, como ids dinâmicos e classes - localize o elemento por seu texto conteúdo) .
A etapa em css é definitivamente mais fácil - pode-se começar a escrever seletores em questão de minutos; mas depois de alguns dias de uso, o poder e as possibilidades do xpath superam rapidamente o css.
E puramente subjetivo - um css complexo é muito mais difícil de ler do que uma expressão xpath complexa.
Outro;)
Finalmente, novamente muito subjetivo - qual escolher?
IMO, não há escolha certa ou errada - são soluções diferentes para o mesmo problema, e o que for mais adequado para o trabalho deve ser escolhido.
Sendo "um fã" do XPath, não tenho vergonha de usar em meus projetos uma mistura dos dois - caramba, às vezes é muito mais rápido apenas lançar um CSS, se eu souber que fará o trabalho muito bem.
fonte
[]
depois,//
por exemplo) . Mas após o primeiro dia de aprendizado e uso, praticamente todo mundo cruza o ponto crítico da curva de aprendizado :) (css entrar é admitidamente mais fácil, IMHO) .O debate entre cssSelector vs XPath permaneceria como um dos debates mais subjetivos na Comunidade Selenium . O que já sabemos até agora pode ser resumido como:
Dave Haeffner realizou um teste em uma página com duas tabelas de dados HTML , uma tabela é escrita sem atributos úteis ( ID e Classe ), e a outra com eles. Eu analisei o procedimento de teste e o resultado deste experimento em detalhes na discussão Por que eu deveria usar seletores cssSelector em vez de XPath para testes automatizados? . Embora esse experimento tenha demonstrado que cada estratégia do localizador é razoavelmente equivalente entre os navegadores, ele não pintou o quadro inteiro de forma adequada para nós. Dave Haeffner na outra discussão Css vs. Caminho X, sob um microscópiomencionado, em um teste de ponta a ponta havia muitas outras variáveis em jogo inicialização do molho , inicialização do navegador e latência de e para o aplicativo em teste. A infeliz conclusão desse experimento pode ser que um driver pode ser mais rápido do que o outro (por exemplo, IE x Firefox ), quando na verdade, esse não era o caso. Para ter uma ideia real de qual é a diferença de desempenho entre cssSelector e XPath, precisávamos cavar mais fundo. Fizemos isso executando tudo em uma máquina local enquanto usamos um utilitário de benchmarking de desempenho. Também nos concentramos em uma ação específica do Selenium, em vez de todo o teste executado, e executamos as coisas várias vezes. Eu analisei o procedimento de teste específico e o resultado dessa experiência em detalhes na discussão cssSelector vs XPath para selênio . Mas os testes ainda faltavam um aspecto, ou seja, mais cobertura do navegador (por exemplo, Internet Explorer 9 e 10) e testes em uma página maior e mais profunda.
Dave Haeffner em outra discussão Css vs. X Path, Under a Microscope (Parte 2) menciona, para garantir que os benchmarks necessários sejam cobertos da melhor maneira possível, precisamos considerar um exemplo que demonstra uma página grande e profunda .
Configuração de teste
Para demonstrar este exemplo detalhado, uma máquina virtual Windows XP foi configurada e o Ruby (1.9.3) foi instalado. Todos os navegadores disponíveis e seus drivers de navegador equivalentes para Selenium também foram instalados. Para benchmarking, a biblioteca padrão do Ruby
benchmark
foi usada.Código de Teste
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
Resultados
Em forma de tabela:
Em forma de gráfico:
Analisando os resultados
Resumo
Curiosidades
Você pode realizar o benchmarking sozinho, usando esta biblioteca onde Dave Haeffner empacotou todo o código.
fonte