Che*_*tan 71 selenium xpath webdriver css-selectors selenium-webdriver
我正在使用Selenium WebDriver 2.25.0进行多语言Web应用程序,主要测试页面内容(适用于阿拉伯语,英语,俄语等不同语言).
对于我的应用程序,根据性能更好,并确保它应该支持所有的浏览器(即IE 7,8,9,FF,Chrome等).
提前感谢您的宝贵建议.
nil*_*esh 87
CSS选择器的性能远远优于Xpath,它在Selenium社区中得到了很好的记录.这是一些原因,
但是在某些情况下,您需要使用xpath,例如,搜索父元素或按文本搜索元素(我不建议稍后使用).
你可以在这里阅读Simon的博客.他还建议使用CSS而不是Xpath.
如果您正在测试内容,则不要使用依赖于元素内容的选择器.这将成为每个地区的维护噩梦.尝试与开发人员交谈,并使用他们用于外化应用程序中文本的技术,如词典或资源包等.这是我的博客,详细解释了它.
感谢@parishodak,这里的链接提供了证明CSS性能更好的数字
Tod*_*kov 31
我要保持不受欢迎的SO硒标签认为XPATH 最好到,从长远来看CSS.
让我先解决"房间里的大象" - xpath比css慢.
使用当前的cpu功率(读取:过去5年中生产的任何东西),甚至是browserstack/saucelabs虚拟机,以及浏览器的开发(阅读:过去5年中所有流行的浏览器),情况并非如此.浏览器的引擎已经开发出来了,xpath的支持是统一的,IE是不合适的(希望对我们大多数人来说).在另一个答案中的这种比较被引用到了所有地方,但它是非常上下文的 - 有多少人在运行 - 或关心 - 反对IE8?
如果存在差异,则只需几分之一毫秒.
然而,大多数更高级别的框架在原始selenium调用上增加了至少1ms的开销(包装器,处理程序,状态存储等); 我选择的个人武器 - 机器人框架 - 增加至少2毫秒,我非常乐意为它所提供的东西做出牺牲.从AWS us-east-1到BrowserStack的集线器的网络往返通常是11毫秒.
因此,对于远程浏览器,如果xpath和css之间存在差异,那么其他所有内容都会黯然失色.
没有那么多的公开比较(我真的看过引用的那个),所以 - 这是一个粗略的单例,虚拟和简单的.目标 - 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))
Run Code Online (Sandbox Code Playgroud)
对于那些不熟悉python的人 - 它打开页面,找到元素 - 首先使用css定位器,然后使用xpath; 查找操作重复1,000次.输出是1,000次重复的总时间(秒),以及一次查找的平均时间(以毫秒为单位).
定位器是 - 对于xpath - "具有这个确切类值的div元素,在DOM中的某个位置"; css类似 - "这个类的div元素,在DOM中的某个地方".故意选择不要过度调整; 此外,类选择器被引用为css为"id之后的第二快".
环境 - Chrome v66.0.3359.139,chromedriver v2.38,cpu:ULV Core M-5Y10通常以1.5GHz运行(是的,"文字处理"一个,甚至不是普通的i7野兽).
这是输出:
css总时间1000次重复:8.84s,每次发现:8.84ms
xpath 1000次重复的总时间:8.52s,每次查找:8.52ms
显然,每个发现的时间非常接近; 差为0 0.32 毫秒.不要跳 - xpath更快 - 有时它是,有时它是css.
让我们尝试使用另一组定位器,这是一个更复杂的定位器 - 一个具有子串的属性(至少对我来说是常见的方法,当一个元素的一部分具有功能意义时,它会追随元素的类):
css total time 1000 repeats: 8.84s, per find: 8.84ms
xpath total time for 1000 repeats: 8.52s, per find: 8.52ms
Run Code Online (Sandbox Code Playgroud)
这两个定位器在语义上也是相同的 - "找到一个div元素,在它的class属性中有这个子串".结果如下:
css总时间1000次重复:8.60s,每次发现:8.60ms
xpath总重复时间1000次:8.75s,每次查找:8.75ms
差值为0.15ms.
使用更复杂的DOM,结果是一样的; 我手边没有任何可公开提供的URL来举例说明,但是再次看到xpath和css的类似结果.
作为练习 - 与评论/其他答案中的链接博客相同的测试- 测试页面是公开的,测试代码也是如此.
他们在代码中执行了一些操作 - 单击列以对其进行排序,然后获取值,并检查UI排序是否正确.我会削减它 - 只需得到定位器,毕竟这是根测试,对吧?
与上面相同的代码,包括以下变化:
网址现在http://the-internet.herokuapp.com/tables; 有2个测试.
第一个定位器 - "按ID和类查找元素" - 是:
xpath_locator = '//div[contains(@class, "button-section")]'
css_locator = 'div[class~=button-section]'
Run Code Online (Sandbox Code Playgroud)
结果:
css总时间1000次重复:8.24s,每次发现:8.24ms
xpath总重复时间为1000次:8.45s,每次查找:8.45ms
差值为0.2毫秒.
"通过遍历寻找元素":
css total time 1000 repeats: 8.60s, per find: 8.60ms
xpath total time for 1000 repeats: 8.75s, per find: 8.75ms
Run Code Online (Sandbox Code Playgroud)
结果:
css总时间1000次重复:9.29s,每次发现:9.29ms
xpath 1000次重复的总时间:8.79s,每次查找:8.79ms
这次是0.5毫秒(相反,xpath在这里变得"更快").
那么,出于xpath和css,两者中哪一个选择性能?答案很简单 - 选择id来定位.
简而言之,如果元素的id是唯一的(因为它应该根据规范),它的值在浏览器的DOM内部表示中起着重要作用,因此通常是最快的.
随着性能的提升,为什么我认为xpath更好?简单 - 通用性和动力.
Xpath是为处理XML文档而开发的语言; 因此,它允许比css更强大的构造.
例如,在树中的每个方向导航 - 找到一个元素,然后转到其祖父母并搜索具有某些属性的子元素.它允许嵌入式布尔条件 - cond1而不是cond2或不cond3(cond3和cond4)); 嵌入式选择器 - "找到具有这些属性的div,然后根据它进行导航".它允许基于节点的值进行搜索 - 但是对这种做法不满意,它确实派上用场,特别是在结构错误的文档中(没有确定的属性可以踩,如动态id和类).
css的踩踏肯定更容易 - 人们可以在几分钟内开始编写选择器; 但经过几天的使用,xpath的功能和可能性很快就克服了css.
纯粹是主观的 - 复杂的css比复杂的xpath表达更难阅读.
最后,再次非常主观 - 选择哪一个?恕我直言,没有正确或错误的选择 - 他们是同一问题的不同解决方案,应该选择更适合工作的任何东西.作为xpath的粉丝,我并不害羞地在我的项目中使用两者的混合 - 哎呀,有时候只要扔掉一个css就快得多,如果我知道它会做得很好.
cssSelector与XPath之间的辩论将仍然是Selenium社区中最主观的辩论之一。到目前为止,我们已经可以概括为:
Dave Haeffner在具有两个HTML数据表的页面上进行了测试,其中一个表编写时没有有用的属性(ID和Class),而另一个表则没有帮助。我在讨论中详细分析了测试过程和该实验的结果,为什么我应该使用cssSelector选择器而不是XPath进行自动测试?。尽管该实验表明每种“ 定位器策略”在浏览器中都相当等效,但并不能为我们充分描绘出整体情况。Dave Haeffner在其他讨论中 Css Vs中。X路径,在显微镜下提到,在端到端测试中,Sauce启动,浏览器启动以及被测试应用程序之间的等待时间还有很多其他变量。该实验的不幸结果可能是,一个驱动程序可能比另一个驱动程序更快(例如IE vs Firefox),但实际上并非如此。真正了解cssSelector和XPath之间的性能差异是什么,我们需要更深入地研究。我们通过使用性能基准测试实用程序在本地计算机上运行所有程序来做到这一点。我们还专注于特定的Selenium操作,而不是整个测试运行,并且运行了无数次。我已经在讨论硒的cssSelector与XPath的讨论中详细分析了具体的测试程序和该实验的结果。但是测试仍然缺少一个方面,即更多的浏览器覆盖范围(例如Internet Explorer 9和10)以及针对更大更深页面的测试。
戴夫·哈夫纳(Dave Haeffner)在另一个讨论中。提到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
Run Code Online (Sandbox Code Playgroud)
注意:输出以秒为单位,结果为100次执行的总运行时间。
以表格形式:
以图表形式:
您可以使用该库自行执行基准测试,Dave Haeffner打包了所有代码。
| 归档时间: |
|
| 查看次数: |
88256 次 |
| 最近记录: |