selenium CSS和XPath选择器之间有什么区别?在跨浏览器测试方面,哪一个更好?

c2e8gylq  于 2022-11-24  发布在  其他
关注(0)|答案(3)|浏览(175)

我正在一个多语言Web应用程序上使用Selenium WebDriver 2.25.0,主要测试页面内容(针对不同的语言,如阿拉伯语、英语、俄语等)。
对于我的应用程序,根据性能,哪个更好,并确保它应该支持所有浏览器(即,Internet Explorer 789,Firefox,Chrome等)?

ff29svar

ff29svar1#

CSS选择器比XPath选择器的性能好得多,Selenium社区中有很好的文档。下面是一些原因:

  • 每个浏览器中的XPath引擎都不相同,因此导致它们不一致
  • Internet Explorer没有本机XPath引擎,因此Selenium为了与其API兼容而注入了自己的XPath引擎。因此,我们失去了使用WebDriver固有的浏览器功能的优势。
  • 在我看来,XPath表达式往往会变得复杂,因此很难阅读

但是,在某些情况下需要使用XPath选择器,例如,搜索父元素或按文本搜索元素(我不推荐后者)。
您可以阅读Simon here的博客。他还建议使用CSS而不是XPath。
如果你正在测试内容,那么不要使用依赖于元素内容的选择器。这将是每一个语言环境的维护噩梦。试着和开发人员谈谈,并使用他们用来在应用程序中外部化文本的技术,比如字典或资源包等。下面是my blog post的详细解释。
多亏了parishodak,这里有the link,它提供了证明CSS性能更好的数字。

ca1c2owp

ca1c2owp2#

我将持有不受欢迎的关于SO Selenium标记的观点,即从长远来看,XPath选择器比CSS选择器更可取。
这篇长文章有两个部分--首先,我将在餐巾纸背面证明这两种方法之间的性能差异是
0.1-0.3毫秒***(是的;这是100**微秒)*,然后我将分享我对为什么XPath更强大看法。

性能差异

让我们首先解决“房间里的大象”-XPath比CSS慢。
以当前CPU功率 (读取:自2013年以来x86生产的任何产品),甚至在BrowserStackSauce LabsAWS虚拟机上,以及浏览器的开发 (请阅读:过去五年中所有流行的歌曲) 情况几乎不是这样的。
浏览器的引擎已经发展起来了,XPath的支持是统一的,Internet Explorer已经不存在了 (希望对我们大多数人来说是这样)。另一个答案中的这种比较到处都在引用,但它是非常上下文相关的--有多少人正在运行--或关心--Internet Explorer 8的自动化?
如果存在差异,则为 * 毫秒的几分之一 *。
然而,大多数高级框架无论如何都会在原始selenium调用上增加至少1 ms的开销( Package 器、处理程序、状态存储等);我个人选择的武器-Robot Framework-至少增加了2毫秒,我非常乐意牺牲这一点。从AWS US-East-1到BrowserStack集线器的网络往返通常是11毫秒
因此,对于远程浏览器,如果XPath和CSS之间存在差异,那么在数量级上,它会被其他一切所掩盖。
测量结果
没有那么多的公共比较 (我真的只看到了引用的一个),所以-这里有一个粗略的单一情况下,虚拟和简单的一个。
它将通过两个策略X次定位一个元素,并比较其平均时间。
目标- BrowserStack的登录页面及其“Sign Up”按钮;撰写此帖子时HTML内容的屏幕截图:

下面是测试代码(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))
  • 对于那些不熟悉Python的人--它打开页面,找到元素--首先用CSS定位器,然后用XPath定位器;查找操作重复1,000次。输出是1,000次重复的总时间(秒)和一次查找的平均时间(毫秒)。*

货位包括:

  • 对于XPath -“在DOM中的某个位置,具有此精确类值的 div 元素“;
  • CSS是类似的-“一个 div 元素与这个类,在DOM的某处”。

它被有意地选择为不被过度调谐;而且,类选择器被引用为CSS中“仅次于id的第二快的”。
环境- Chrome浏览器v66.0.3359.139,ChromeDriver v2.38,CPU:ULV酷睿M-5 Y10通常以1.5 GHz * 运行(是的,是一款“文字处理”处理器,甚至不是普通的酷睿i7)*。
输出如下:

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

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

显然,每次查找的时间非常接近;差别是0.32*毫秒 *。不要跳过“XPath选择器更快”-有时是这样,但有时是CSS。
让我们试试另一组定位符,它稍微复杂一点--一个属性有一个子字符串 (至少对我来说是一种常见的方法,当元素的一部分有功能意义时,就在元素的类后面)

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

这两个定位符在语义上也是相同的--“找到一个在其类属性中具有这个子串的 div 元素”。
结果如下:

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

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

相差0.15 ms
作为一个练习(与注解/其他答案中的linked blog测试相同),test page是公共的,testing code也是。
他们在代码中做了几件事--单击一个列以按它排序,然后获取值,并检查UI排序是否正确。
我会剪掉它--只要拿到定位器,毕竟--这是根测试,对吧?
代码与上面的代码相同,但在以下方面进行了更改:

  • URL现在是http://the-internet.herokuapp.com/tables;有两个测试。
  • 第一个定位器“按ID和类查找元素”的定位器为:
css_locator = '#table2 tbody .dues'
xpath_locator = "//table[@id='table2']//tr/td[contains(@class,'dues')]"

结果如下:

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

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

相差0.2毫秒。
“通过遍历查找图元”:

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

结果:

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

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

这一次是0.5ms(反过来,XPath在这里“更快”)。
因此,五年后(更好的浏览器引擎)* 和 * 只关注定位器性能(没有UI中的排序等操作),相同的测试床-CSS和XPath之间实际上没有区别。
那么,在XPath和CSS中,选择哪一种来提高性能呢?答案很简单--选择locating by id
长话短说,如果元素的id是唯一的(根据规范它应该是唯一的),那么它的值在浏览器的DOM内部表示中扮演着重要的角色,因此通常是最快的。
然而,唯一的和恒定的 (例如,不是自动生成的) id并不总是可用的,这就引出了“如果有CSS,为什么要使用XPath?”

XPath的优势

在性能不佳的情况下,为什么我认为XPath更好呢?很简单--多功能性和强大的功能。

XPath是为处理XML文档而开发的语言;因此,它允许使用比CSS更强大的结构。
例如,在树中的每个方向上导航-找到一个元素,然后转到它的祖父,并搜索它的具有特定属性的子元素。嵌入式选择器-“找到一个div,它包含具有这些属性的这些子元素,然后根据它进行导航”。
XPath允许基于节点的值(其文本)进行搜索--尽管这种做法并不令人满意。它确实很方便,尤其是在结构不好的文档中 (没有明确的属性可供使用,如动态id和类--通过文本内容定位元素)
CSS中的单步执行无疑更容易-只需几分钟就可以开始编写选择器;但经过几天的使用,XPath的强大功能和可能性很快就战胜了CSS。
而且完全是主观的--复杂的CSS表达式比复杂的XPath表达式更难阅读。

结尾;)

最后,又是一个非常主观的问题--我们应该选择哪一个?
我认为,没有什么正确或错误的选择--它们是同一个问题的不同解决方案,应该选择更适合这项工作的方案。
作为XPath的“爱好者”,我并不羞于在我的项目中混合使用这两种方法--见鬼,有时候如果我知道CSS方法可以很好地完成工作,那么直接使用CSS方法会快得多。

4ioopgfo

4ioopgfo3#

  • cssSelector* 与 XPath 之间的争论仍然是 *Selenium社区 * 中最主观的争论之一。
  • 支持 cssSelector 的人说,它的可读性更强,速度更快(特别是在Internet Explorer上运行时)。
  • 而那些支持 XPath 的人则吹嘘它能够遍历页面(而 cssSelector 则不能)。
  • 在IE8等较旧的浏览器中遍历DOM不适用于 cssSelector,但适用于 XPath
  • XPath 可以在DOM中向上遍历(例如,从子对象到父对象),而 cssSelector 只能在DOM中向下遍历(例如,从父对象到子对象)
  • 然而,在旧版浏览器中无法使用 cssSelector 遍历DOM并不一定是坏事,因为这更像是一个指示器,表明您的页面设计不佳,可以从一些有用的标记中获益。
  • Ben Burton 提到你应该使用 cssSelector,因为这是构建应用程序的方式,这使得测试更容易编写、讨论和让其他人帮助维护。
  • Adam Goucher 建议采用一种更加混合的方法--首先关注ID,然后关注 cssSelector,只在需要时才利用 XPath(例如,遍历DOM),而且 XPath 对于高级定位器来说总是更强大。
  • Dave Haeffner* 在a page with two HTML data tables上执行了test,一个表没有有用的属性(ID 和 * 类别 *),我已经在讨论中详细分析了 * 测试过程 * 和这个实验的结果为什么我应该使用 cssSelector 选择器而不是XPath来进行自动化测试?。虽然这个实验证明了每种定位器策略在不同浏览器之间是相当等效的,但它并没有为我们描绘出全貌。Dave Haeffner 在另一个讨论Css Vs. X Path, Under a Microscope中提到,在一个端到端测试中,有很多其他的变量在起作用 * 酱料启动 浏览器启动 *,和 * 延迟 。这个实验的不幸结果是一个驱动程序可能比另一个驱动程序快(例如,IEFirefox),而事实上,情况并非如此。为了真实的体验 cssSelectorXPath 之间的性能差异,我们需要更深入地研究。我们通过使用性能基准测试工具从本地机器运行所有内容来实现这一点。我们还关注了特定的Selenium操作,而不是整个测试运行。我已经在讨论cssSelector与XPath for selenium时详细分析了具体的 * 测试过程 * 和这个实验的结果。但是测试仍然缺少一个方面,即 * 更多的浏览器覆盖率(例如,Internet Explorer 9和10)和针对更大更深的页面进行测试。
  • Dave Haeffner* 在另一次讨论Css Vs. X Path, Under a Microscope (Part 2)时提到,为了确保以最佳方式涵盖所需的基准测试,我们需要考虑an example that demonstrates a large and deep page

测试设置

为了演示这个详细的示例,我们设置了一个Windows XP虚拟机,并安装了Ruby (1.9.3)。所有可用的浏览器及其用于Selenium的等效浏览器驱动程序也都安装了。为了进行基准测试,使用了Ruby的标准库benchmark

测试代码

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

结果数

注意:输出以秒为单位,结果是针对100次执行得总运行时间.

表格形式:

图表形式:

*

*火狐浏览器

*互联网浏览器8

*互联网浏览器9

x1c4d 1x指令集

*互联网浏览器10

*歌剧


指令集

分析结果

  • Chrome和Firefox经过了明确的优化,可实现更快的 cssSelector 性能。
  • Internet Explorer 8是一个不起作用的 cssSelector 的垃圾袋,一个失控的 XPath 遍历需要大约65秒,一个38秒的表遍历没有 cssSelector 结果可供比较。
  • 在IE 9和10中,XPath 总体上更快。在Safari中,除了使用 XPath 的一些较慢的遍历之外,这是一个很大的问题。在几乎所有浏览器中,使用 XPath 进行嵌套同级遍历和表格单元格遍历都是一个昂贵的操作。
  • 这并不令人惊讶,因为定位器是脆弱的和低效的,我们需要避免它们。

摘要

  • 总的来说,在两种情况下,XPath 明显比 cssSelector 慢,但这是很容易避免的。
  • 对于非IE浏览器,性能差异略微有利于css-selectors,对于IE浏览器,略微有利于xpath

琐事

您可以使用this libraryDave Haeffner 封装了所有代码)自行执行基准测试。

相关问题