Watir:使用不同 DOM 选择方法进行阅读所用时间的差异

Watir: Difference in time taken for reading using different DOM selection methods

我正在使用 Watir 来测试我的网络应用程序。我使用 CSS 选择器来访问各种元素。在下面的示例中,我试图从 table.

中读取所有数据

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

在第二种方法中,我使用选择器读取每个单元格。我很惊讶第二个比第一个快三倍左右。

方法一:

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

方法二:

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 选择器。

这是为什么?

rows是一种方法。将其结果分配给一个变量,您将不会有那么多电汇电话。您还可以通过执行以下操作查看底层请求:Selenium:WebDriver.logger.level = :info

问题

决定性能的最大因素是有线调用的次数 - 即 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 元素。 table 越大,第二种方法节省的时间越多。

解决方案 - 使用一行中的几个单元格

如果您只是挑选出一行中的几个单元格,您可以通过定位特定的 td 而不使用集合调用来避免使用 CSS 选择器:

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

这提供了与 CSS-选择器类似的性能。对于获取包含 25 个 td 元素的单个 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