96

我正在使用 Selenium WebDriver 2.25.0 开发多语言 Web 应用程序,主要测试页面内容(针对不同的语言,如阿拉伯语、英语、俄语等)。

对于我的应用程序,根据性能并确保它应该支持所有浏览器(即 IE 7、8、9、FF、Chrome 等)。

提前感谢您的宝贵建议。

4

3 回答 3

116

CSS 选择器的性能比 Xpath 好得多,并且在 Selenium 社区中有很好的记录。这里有一些原因,

  • Xpath 引擎在每个浏览器中都不同,因此使它们不一致
  • IE 没有原生的 xpath 引擎,因此 selenium 注入了自己的 xpath 引擎以兼容其 API。因此,我们失去了使用 WebDriver 固有的原生浏览器特性的优势。
  • Xpath 往往会变得复杂,因此在我看来很难阅读

但是在某些情况下,您需要使用 xpath,例如,搜索父元素或通过文本搜索元素(我不推荐后者)。

你可以在这里阅读 Simon 的博客。他还推荐使用 CSS 而不是 Xpath。

如果您正在测试内容,则不要使用依赖于元素内容的选择器。这将是每个地区的维护噩梦。尝试与开发人员交谈并使用他们用来将应用程序中的文本外部化的技术,例如字典或资源包等。这是我的博客,详细解释了它。

编辑 1

感谢@parishodak,这里是提供证明CSS性能更好的数字的链接

于 2013-05-28T14:30:23.990 回答
56

我将持有不受欢迎的 SO selenium 标签观点,即从长远来看, XPath 比 CSS 更可取。

这篇长篇文章有两个部分——首先我会做一个简单的证明,证明两者之间的性能差异是0.1-0.3 毫秒 (是的;那是 100微秒,然后我会分享我的观点为什么XPath 更强大。


性能差异

让我们首先解决“房间里的大象”——xpath 比 css 慢。

以当前的 cpu 能力(阅读:自 2013 年以来生产的任何 x86 产品),即使在 browserstack/saucelabs/aws VM 上,以及浏览器的发展(阅读:过去 5 年中所有流行的浏览器),情况几乎都不是这样。浏览器的引擎已经开发,对 xpath 的支持是统一的,IE 是不可能的(希望对我们大多数人来说)。其他答案中的这种比较被到处引用,但它是非常有上下文的——有多少人正在运行——或关心——针对 IE8 的自动化?

如果存在差异,则在几分之一毫秒内

然而,大多数高级框架无论如何都会比原始 selenium 调用增加至少 1ms 的开销(包装器、处理程序、状态存储等);我个人选择的武器——RobotFramework——增加了至少 2 毫秒,我很乐意为它提供的东西牺牲。从 AWS us-east-1 到 BrowserStack 中心的网络往返通常为11 毫秒

因此,对于远程浏览器,如果 xpath 和 css 之间存在差异,它会被其他所有内容所掩盖,数量级。


测量结果

没有那么多公开的比较(我真的只看到过引用的比较),所以——这里有一个粗略的单一案例,虚拟的和简单的。
它将通过两种策略 X 次定位一个元素,并比较平均时间。

目标——BrowserStack 的登陆页面,以及它的“注册”按钮;写这篇文章时的 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 Core M-5Y10 通常运行在 1.5GHz (是的,一个“文字处理”,甚至不是普通的 i7 野兽)

这是输出:

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

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

显然,每次查找的时间非常接近;差异是0.32 毫秒。不要跳“xpath 更快”——有时是,有时是 css。


让我们尝试使用另一组定位器,稍微复杂一点 - 一个具有子字符串的属性(至少对我来说是常用方法,当元素的一部分具有功能意义时,它会追踪元素的类)

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

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

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

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

0.15ms的差异。


作为一个练习 - 与评论/其他答案中链接博客中所做的相同测试-测试页面是公开的,测试代码也是如此。

他们在代码中做了几件事——点击列进行排序,然后获取值,并检查 UI 排序是否正确。
我会删掉它——毕竟只是得到定位器——这是根本测试,对吧?

与上面相同的代码,其中有以下更改:

  • 网址现在是http://the-internet.herokuapp.com/tables;有2个测试。

  • 第一个定位器 - “按 ID 和类查找元素” - 是:

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

结果如下:

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

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

差异为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.29s, per find: 9.29ms

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

这次是0.5毫秒(相反,xpath 在这里变得“更快”)。

所以 5 年后(更好的浏览器引擎)并且只关注定位器的性能(没有在 UI 中排序等操作),相同的测试平台 - CSS 和 XPath 之间几乎没有区别。


那么,在 xpath 和 css 之外,选择两者中的哪一个来提高性能呢?答案很简单——选择id 定位

长话短说,如果一个元素的 id 是唯一的(因为它应该根据规范),它的值在浏览器的 DOM 内部表示中起着重要作用,因此通常是最快的。

然而,唯一且恒定的(例如,不是自动生成的) id 并不总是可用的,这使我们想到“如果有 CSS,为什么要使用 XPath?”


XPath 的优势

由于性能超出预期,为什么我认为 xpath 更好?简单 – 多功能性和强大的功能。

Xpath 是一种为处理 XML 文档而开发的语言;因此,它允许比 css 更强大的构造。
例如,在树中的每个方向导航——找到一个元素,然后转到它的祖父母并搜索它的具有某些属性的子元素。
它允许嵌入布尔条件 - cond1 and not(cond2 or not(cond3 and cond4)); 嵌入式选择器——“找到一个具有这些属性的子元素的 div,然后根据它进行导航”。
XPath 允许基于节点的值(它的文本)进行搜索——尽管这种做法令人不快,但它确实派上用场,尤其是在结构糟糕的文档中(没有明确的属性可供踩踏,如​​动态 id 和类——通过其文本定位元素内容)

步入 css 肯定更容易——几分钟就可以开始编写选择器;但是经过几天的使用,xpath 的功能和可能性很快就超越了 css。
而且纯粹是主观的——复杂的 css 比复杂的 xpath 表达式更难阅读。

结束;)

最后,再次非常主观 - 选择哪一个?

IMO,没有正确或错误的选择-它们是同一问题的不同解决方案,应该选择更适合该工作的方法。

作为 XPath 的“粉丝”,我并不羞于在我的项目中同时使用这两种方法——哎呀,有时只扔一个 CSS 会快得多,如果我知道它可以很好地完成工作的话。

于 2018-05-11T12:12:59.153 回答
18

cssSelectorXPath之间的争论仍然是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包含两个 HTML 数据表的页面进行了测试,其中一个表没有有用的属性(IDClass),另一个则包含它们。我已经在讨论中详细分析了测试过程和该实验的结果为什么我应该使用cssSelector选择器而不是 XPath 进行自动化测试?. 虽然这个实验证明了每个定位器策略在浏览器之间是相当等价的,但它并没有为我们充分描绘出整个画面。Dave Haeffner在其他讨论Css Vs. X 路径,显微镜下提到,在端到端测试中,还有很多其他变量在起作用Sauce startupBrowser start up以及与被测应用程序之间的延迟。不幸的是,从该实验中得出的结论可能是一个驱动程序可能比另一个驱动程序更快(例如IEFirefox),而事实上,情况并非如此。真正体验cssSelectorXPath之间的性能差异,我们需要深入挖掘。我们通过使用性能基准测试实用程序从本地计算机运行所有内容来做到这一点。我们还专注于特定的 Selenium 操作而不是整个测试运行,并且多次运行。我已经在讨论cssSelector vs XPath for selenium中详细分析了这个实验的具体测试过程和结果。但是测试仍然缺少一个方面,即更多的浏览器覆盖率(例如,Internet Explorer 9 和 10)以及针对更大和更深页面的测试。

Dave Haeffner在另一个讨论Css Vs. X 路径,在显微镜下(第 2 部分)提到,为了确保以最佳方式覆盖所需的基准,我们需要考虑一个演示大而深页面的示例


测试设置

为了演示这个详细的示例,安装了 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 次执行的总运行时间。

以表格形式:

css_xpath_under_microscopev2

图表形式:

图表铬

  • 火狐

图表-火狐

  • 互联网浏览器 8

图表-ie8

  • 互联网浏览器 9

图表-ie9

  • 互联网浏览器 10

图表-ie10

  • 歌剧

图表歌剧


分析结果

  • Chrome 和 Firefox 明显针对更快的cssSelector性能进行了调整。
  • Internet Explorer 8 是一个无法工作的cssSelector 抓包,一个需要约 65 秒的失控XPath遍历,以及一个 38 秒的表遍历,没有cssSelector结果与之进行比较。
  • 在 IE 9 和 10 中,XPath总体上更快。在 Safari 中,这是一个折腾,除了使用XPath的几个较慢的遍历运行。在几乎所有浏览器中,使用XPath完成的嵌套兄弟遍历表格单元遍历都是一项昂贵的操作。
  • 这些不应该那么令人惊讶,因为定位器脆弱且效率低下,我们需要避免它们。

概括


琐事

您可以使用 Dave Haeffner 打包所有代码的这个库自行执行基准测试

于 2019-02-09T12:58:53.753 回答