Watir:使用不同DOM选择方法读取时间的差异

时间:2017-07-31 14:09:11

标签: ruby performance css-selectors watir

我正在使用Watir来测试我的Web应用程序。我使用CSS选择器来访问各种元素。在下面的例子中,我试图从表中读取所有数据。

在第一种方法中,我获取所有表行,然后从行中的每个单元格中读取文本。

在第二种方法中,我正在使用其选择器读取每个单元格。令我感到惊讶的是,第二个比第一个快了大约三倍。

方法1:

rows = $browser.table(:id,"bin_die_count").rows
while index < rows.count
    bin_details = {}
    speed_grade = rows[index][2].text
    die_count = rows[index][3].text
    bin_value = rows[index][0].text
    bin_details = {"speed_grade" => speed_grade, "die_count" => die_count}
    all_bin_details[bin_value] = bin_details
    puts bin_details
    index = index + 1
end

方法2:

row_count = $browser.table(:id,"bin_die_count").rows.count
while index < rows.count
    bin_details = {}
    speed_grade =  $browser.element(:css,"#bin_die_count > tbody > tr:nth-child(#{index}) > td:nth-child(3)").text
    die_count = $browser.element(:css,"#bin_die_count > tbody > tr:nth-child(#{index}) > td:nth-child(4)").text
    bin_value = $browser.element(:css,"#bin_die_count > tbody > tr:nth-child(#{index}) > td:nth-child(1)").text
    bin_details = {"speed_grade" => speed_grade, "die_count" => die_count}
    all_bin_details[bin_value] = bin_details
    puts bin_details
    index = index + 1
end

此处方法1完成46.555秒,方法2耗时16.025秒。 我期望方法1更快,因为它引用相对于行的文本,但方法2引用具有绝对CSS选择器的每个文本。

为什么会这样?

2 个答案:

答案 0 :(得分:4)

<强>问题

决定性能的最大因素是有线电话的数量 - 即Watir要求Selenium与浏览器通话的次数。

在我简化为rows[0][0].text的第一种方法中,您会看到以下8个电话:

2017-07-31 11:45:37 INFO Selenium -> POST session/a248a69dfd9ae930072e4a3dbe5a979f/elements
2017-07-31 11:45:37 INFO Selenium    >>> http://127.0.0.1:9515/session/a248a69dfd9ae930072e4a3dbe5a979f/elements | {"using":"tag name","value":"tr"}
2017-07-31 11:45:37 INFO Selenium <- {"sessionId":"a248a69dfd9ae930072e4a3dbe5a979f","status":0,"value":[{"ELEMENT":"0.9824074557261091-1"},{"ELEMENT":"0.9824074557261091-2"}]}
2017-07-31 11:45:37 INFO Selenium -> GET session/a248a69dfd9ae930072e4a3dbe5a979f/element/0.9824074557261091-1/enabled
2017-07-31 11:45:37 INFO Selenium <- {"sessionId":"a248a69dfd9ae930072e4a3dbe5a979f","status":0,"value":true}
2017-07-31 11:45:37 INFO Selenium -> POST session/a248a69dfd9ae930072e4a3dbe5a979f/element/0.9824074557261091-1/elements
2017-07-31 11:45:37 INFO Selenium    >>> http://127.0.0.1:9515/session/a248a69dfd9ae930072e4a3dbe5a979f/element/0.9824074557261091-1/elements | {"using":"xpath","value":"./th | ./td"}
2017-07-31 11:45:37 INFO Selenium <- {"sessionId":"a248a69dfd9ae930072e4a3dbe5a979f","status":0,"value":[{"ELEMENT":"0.9824074557261091-3"},{"ELEMENT":"0.9824074557261091-4"},{"ELEMENT":"0.9824074557261091-5"},{"ELEMENT":"0.9824074557261091-6"}]}
2017-07-31 11:45:37 INFO Selenium -> GET session/a248a69dfd9ae930072e4a3dbe5a979f/element/0.9824074557261091-3/name
2017-07-31 11:45:37 INFO Selenium <- {"sessionId":"a248a69dfd9ae930072e4a3dbe5a979f","status":0,"value":"td"}
2017-07-31 11:45:37 INFO Selenium -> GET session/a248a69dfd9ae930072e4a3dbe5a979f/element/0.9824074557261091-4/name
2017-07-31 11:45:37 INFO Selenium <- {"sessionId":"a248a69dfd9ae930072e4a3dbe5a979f","status":0,"value":"td"}
2017-07-31 11:45:37 INFO Selenium -> GET session/a248a69dfd9ae930072e4a3dbe5a979f/element/0.9824074557261091-5/name
2017-07-31 11:45:37 INFO Selenium <- {"sessionId":"a248a69dfd9ae930072e4a3dbe5a979f","status":0,"value":"td"}
2017-07-31 11:45:37 INFO Selenium -> GET session/a248a69dfd9ae930072e4a3dbe5a979f/element/0.9824074557261091-6/name
2017-07-31 11:45:37 INFO Selenium <- {"sessionId":"a248a69dfd9ae930072e4a3dbe5a979f","status":0,"value":"td"}
2017-07-31 11:45:37 INFO Selenium -> GET session/a248a69dfd9ae930072e4a3dbe5a979f/element/0.9824074557261091-3/text
2017-07-31 11:45:37 INFO Selenium <- {"sessionId":"a248a69dfd9ae930072e4a3dbe5a979f","status":0,"value":"Product"}

相比之下,第二种方法又简化为browser.td(:css,"#bin_die_count > tbody > tr:nth-child(1) > td:nth-child(1)").text,只有3个电话:

2017-07-31 11:46:33 INFO Selenium -> POST session/f19ba6fd8cf948b590b36f3c77191624/element
2017-07-31 11:46:33 INFO Selenium    >>> http://127.0.0.1:9515/session/f19ba6fd8cf948b590b36f3c77191624/element | {"using":"css selector","value":"#bin_die_count > tbody > tr:nth-child(1) > td:nth-child(1)"}
2017-07-31 11:46:33 INFO Selenium <- {"sessionId":"f19ba6fd8cf948b590b36f3c77191624","status":0,"value":{"ELEMENT":"0.8228824382277831-1"}}
2017-07-31 11:46:33 INFO Selenium -> GET session/f19ba6fd8cf948b590b36f3c77191624/element/0.8228824382277831-1/name
2017-07-31 11:46:33 INFO Selenium <- {"sessionId":"f19ba6fd8cf948b590b36f3c77191624","status":0,"value":"td"}
2017-07-31 11:46:33 INFO Selenium -> GET session/f19ba6fd8cf948b590b36f3c77191624/element/0.8228824382277831-1/text
2017-07-31 11:46:33 INFO Selenium <- {"sessionId":"f19ba6fd8cf948b590b36f3c77191624","status":0,"value":"Product"}

第二种方法只有不到一半的有线电话,这导致大约一半的时间执行。

看起来第一种方法需要更长时间的主要原因是,在获取td元素的集合时,每个td元素标记名称都会被验证。例如,一个包含3个td元素的行将对该名称进行3次调用,具有4个td元素的行将对该名称进行4次调用,等等。相反,第二种方法只是需要抓取一个特定的td元素。表格越大,第二种方法节省的时间就越多。

解决方案 - 使用连续的几个单元格

如果您只是挑选一行的几个单元格,则可以通过在没有收集调用的情况下找到特定的td来避免使用CSS选择器:

rows[0].td(index: 0).text

这提供了与CSS选择器类似的性能。以下表现可以获得单个td的文本,其中包含25个td元素:

rows = browser.trs
puts Benchmark.measure { 100.times { rows[0][0].text } } 
#=> 45.781881

puts Benchmark.measure { 100.times { browser.td(:css,"#bin_die_count > tbody > tr:nth-child(1) > td:nth-child(1)").text } }
#=> 4.832999

rows = browser.trs
puts Benchmark.measure { 100.times { rows[0].td(index: 0).text } }
#=> 4.812138

解决方案 - 使用连续多个单元格

如果您使用该行的许多td元素,则最好获取元素集合。但是,您应该为行执行一次,而不是每个单元格执行一次。例如:

row_tds = rows[index].tds
speed_grade = row_tds[2].text
die_count = row_tds[3].text
bin_value = row_tds[0].text

正如您在下面的效果结果中所看到的,一次获取整个集合比单独访问每个单元格更快:

rows = browser.trs
puts Benchmark.measure { 20.times { (1..24).map { |i| rows[0].td(index: i).text } } }
#=> 18.776798

rows = browser.trs
puts Benchmark.measure { 20.times { tds = rows[0].tds; tds.map(&:text) } }
#=> 13.478259

答案 1 :(得分:0)

rows是一种方法。将其结果分配给变量,您将无法获得尽可能多的有线电话。您还可以通过执行以下操作查看基础请求:Selenium:WebDriver.logger.level = :info