【问题标题】:Watir wait_until_present times out instead of returning falseWatir wait_until_present 超时而不是返回 false
【发布时间】:2015-05-11 00:44:58
【问题描述】:

我对使用 Watir 进行测试仍然是新手,但我一直在阅读有关 waiting with Watir 的所有内容,但找不到解决问题的方法。

基本上我希望wait_while_presentwait_until_present 在页面加载时工作。当我意识到两者都超时时,我尝试使用 element.present? 编写我自己的 janky 版本,但也超时了。我认为这可能是当前版本的 watir-webdriver 的问题。

这是一个给我带来很多麻烦的测试示例:

def test_rows_per_page()
  begin
    browser.element(:id => 'moar-rows').when_present.click
    browser.element(:id => 'loading').wait_while_present
    assert_text = browser.element(:id => 'num-rows').text

    browser.element(:id => 'less-rows').when_present.click
    browser.element(:id => 'loading').wait_while_present
    diff_text = browser.element(:id => 'num-rows').text

    if !(assert_text.eql? diff_text)
      screen_capture()
      assert_passed()
    else
      screen_capture()
      assert_failed()
    end
  rescue
    screen_capture()
    increment_failed() #this is how I know it's timing out
  end
end

我尝试测试puts load_element.present?(timeout=1),但它失败得更快。因此,wait_until_present 在元素不存在时不会返回 false。 如何让它返回 false?

我可能会尝试这个,这是对我一开始链接的问题的回应,但我不确定它是否能解决我的问题:

Timeout::timeout(input_timeout=1) do
  #Default webdriver command here
end
rescue Timeout::Error
  raise TimeoutError
end

所以我不会让我发布两个以上的链接,因为我的声誉非常低,但 watir-webdriver 的源代码在 GitHub 上。


我忘了把显示超时的具体上下文放在哪里:

def test_rows_per_page()
  begin
    browser.element(:id => 'moar-rows').when_present.click
    loading_element = element(:id => 'loading')
    #loading_element.wait_while_present
    assert_text = browser.element(:id => 'num-rows').text

    browser.element(:id => 'less-rows').when_present.click
    #loading_element.wait_while_present
    puts loading_element.present? #=> true
    sleep 1
    puts loading_element.present? #times out here
    sleep 1
    puts loading_element.present? 

    diff_text = browser.element(:id => 'num-rows').text
    if !(assert_text.eql? diff_text)
      screen_capture()
      assert_passed()
    else
      screen_capture()
      assert_failed()
    end
  rescue
    screen_capture()
    increment_failed() #this is how I know it's timing out
  end
end

注释掉rescue 块,这是错误:

:in `eval': unknown error: Element is not clickable at point (550, 565). Other element would receive the click: <div id="loading-screen" class="loading-page-backdrop ng-scope" ng-if="pageLoading || gridLoading"></div>
(Selenium::WebDriver::Error::UnknownError)
(Session info: chrome=41.0.2272.76)
(Driver info: chromedriver=2.6.232908,platform=Mac OS X 10.10.2 x86_64)
from /Library/Ruby/Gems/2.0.0/gems/selenium-webdriver-2.44.0/lib/selenium/webdriver/remote/response.rb:15:in `initialize'
from /Library/Ruby/Gems/2.0.0/gems/selenium-webdriver-2.44.0/lib/selenium/webdriver/remote/http/common.rb:59:in `new'
from /Library/Ruby/Gems/2.0.0/gems/selenium-webdriver-2.44.0/lib/selenium/webdriver/remote/http/common.rb:59:in `create_response'
from /Library/Ruby/Gems/2.0.0/gems/selenium-webdriver-2.44.0/lib/selenium/webdriver/remote/http/default.rb:66:in `request'
from /Library/Ruby/Gems/2.0.0/gems/selenium-webdriver-2.44.0/lib/selenium/webdriver/remote/http/common.rb:40:in `call'
from /Library/Ruby/Gems/2.0.0/gems/selenium-webdriver-2.44.0/lib/selenium/webdriver/remote/bridge.rb:640:in `raw_execute'
from /Library/Ruby/Gems/2.0.0/gems/selenium-webdriver-2.44.0/lib/selenium/webdriver/remote/bridge.rb:618:in `execute'
from /Library/Ruby/Gems/2.0.0/gems/selenium-webdriver-2.44.0/lib/selenium/webdriver/remote/bridge.rb:375:in `clickElement'
from /Library/Ruby/Gems/2.0.0/gems/selenium-webdriver-2.44.0/lib/selenium/webdriver/common/element.rb:54:in `click'
from /Library/Ruby/Gems/2.0.0/gems/watir-webdriver-0.6.11/lib/watir-webdriver/elements/element.rb:132:in `click'
from /Library/Ruby/Gems/2.0.0/gems/watir-webdriver-0.6.11/lib/watir-webdriver/wait.rb:122:in `method_missing'
from /file.rb:208:in `rowsPerPage_next'

我将尝试browser.div(:id =&gt; 'loading-screen').wait_while_present 看看是否可行。很难从加载页面 HTML 中提取 idclass,因为它仅在加载时才存在。 (我向开发人员询问了标识符,但我不喜欢过多地打扰她。)


更多注释:

我的主要问题是,如果元素不存在,wait_while_present 似乎不会继续。这就是为什么我尝试使用 present? 编写我自己的 janky 版本,但这也是次当元素不存在时,而不是返回false。我宁愿让 Watir 版本按应有的方式工作。

我已经尝试了两种方法,使用 idclass 加载 div,但当 div 不存在时它仍然超时而不是继续。

如果相关的话,我目前有这些全局等待设置:

browser.driver.manage.timeouts.implicit_wait = 30
browser.driver.manage.timeouts.page_load = 30
Watir.default_timeout = 30

present? 应该返回 true 如果元素存在并且可见,所以如果加载 div 不可见,那么它应该只返回 false,对吗?即使它存在但被隐藏?我真的尝试过自己解决这个问题。

请告诉我我疯了,有一些简单的方法可以解决这个问题,比如重新安装 Ruby 之类的。

【问题讨论】:

  • 你得到的超时错误是什么?如果脚本显示您调用.present? 的位置也会有所帮助。
  • 当它超时时转到rescue,所以我只是从increment_failed() 得到输出。我注释掉了wait_while_present 行并输入了puts load_element.present?(timeout=1),它在我为超时设置的任何时间长度后都失败了。
  • 我添加了另一个代码块以获得更多上下文。
  • 我认为在使用rescue 子句进行调试时需要小心。例如,load_element.present?(timeout=1) 由于 ArgumentError 而进入救援子句,present? 不接受任何参数,而不是因为超时。删除rescue(或至少捕获并输出异常)将使您能够查看实际的堆栈跟踪并更好地了解问题所在。
  • 是的,我再次尝试后发现了超时问题。我注释掉了 rescue 块,它抛出了一个错误,请给我一分钟清理并发布它。

标签: ruby testing watir watir-webdriver


【解决方案1】:

我想我会放弃并离开sleeps。

(忽略整个帖子中classid 名称的随机变化。还有snake_casecamelCase。我不是这样写的。相信我已经成功进入有问题的 HTML 元素。)

unknown error: Element is not clickable at point (550, 565). 
Other element would receive the click: <div id="dashboard-loading-screen" class="loading-page-backdrop ng-scope" 
ng-if="pageLoading || gridLoading"></div> (Selenium::WebDriver::Error::UnknownError) (Session info: chrome=41.0.2272.76)

这是整个测试的净化版本,让我发疯。我在调试模式下没有加载问题,因为当然当我在调试时它有足够的时间来加载,所以我不得不抛出一堆puts 来找出它失败的那一行。

另外请注意,我没有编写这个测试的原始版本,我只是想让它正确断言。

def dashboard_rowsPerPage_next(targetRows)
  begin
    dashboard_rowsPerPage_internal(targetRows) #click drop-down to choose num of rows/page
    browser.scroll.to :bottom
    beforeRowsText = $browser.div(:class => 'grid-rows-of-label').when_present.text.split(" ", 4).last #page 1

    #I put this instead of wait_while_present
    if (puts browser.html.include?("loading-page-backdrop ng-scope"))
      Watir::Wait.while { browser.html.include? "loading-page-backdrop ng-scope"}
    end
    puts 'a'
    puts browser.html.include?("loading-page-backdrop ng-scope")
    puts browser.html.include?("loading-page-backdrop ng-scope")

    #here's where I get the error message
    browser.span(:class, "k-icon k-i-arrow-e").when_present.click 
    puts 'b'
    puts browser.html.include?("loading-page-backdrop ng-scope")

    if (puts browser.html.include?("loading-page-backdrop ng-scope"))
      Watir::Wait.while { browser.html.include? "loading-page-backdrop ng-scope"}
    end

    puts browser.html.include?("loading-page-backdrop ng-scope")
    puts 'c'
    puts browser.html.include?("loading-page-backdrop ng-scope")
    browser.scroll.to :bottom
    puts browser.html.include?("loading-page-backdrop ng-scope")
    afterRowsText = browser.div(:class => 'grid-rows-of-label').when_present.text.split(" ", 4).last #page 2
    puts 'd'
    Watir::Wait.while { browser.html.include? "dashboard-loading-screen"}

    if (!(beforeRowsText.eql? afterRowsText)) #page 2 text should not be the same as page 1 text
      screenCapture()
      assertPassed()
    else
      screenCapture()
      assertFailed()
    end
#  rescue
#    screenCapture()
#    increment_failed()
  end
end

所以我的输出是:(为清楚起见添加了 cmets)

true #should wait while loading image is visible before puts 'a'
a
true #this means the loading page is still visible, while isn't working
true
unknown error: Element is not clickable at point (550, 565). 
Other element would receive the click: <div id="dashboard-loading-screen" class="loading-page-backdrop ng-scope" 
ng-if="pageLoading || gridLoading"></div> (Selenium::WebDriver::Error::UnknownError) (Session info: chrome=41.0.2272.76)

我检查了 beforeRowsText 和 afterRowsText 的值,它正确地获取了这些值。

所以我打算这样做,因为我很弱:

def dashboard_rowsPerPage_next(targetRows)
  begin
    dashboard_rowsPerPage_internal(targetRows) #click drop-down to choose num of rows/page
    browser.scroll.to :bottom
    beforeRowsText = $browser.div(:class => 'grid-rows-of-label').when_present.text.split(" ", 4).last #page 1
    sleep 10
    browser.span(:class, "k-icon k-i-arrow-e").when_present.click 
    sleep 10
    browser.scroll.to :bottom
    afterRowsText = browser.div(:class => 'grid-rows-of-label').when_present.text.split(" ", 4).last #page 2

    if (!(beforeRowsText.eql? afterRowsText)) #page 2 text should not be the same as page 1 text
      screenCapture()
      assertPassed()
    else
      screenCapture()
      assertFailed()
    end
  rescue
    screenCapture()
    increment_failed()
  end
end

等一下,没关系。它适用于每页 25 行,但我在每页 50 行时遇到相同的错误。

【讨论】:

  • 您使用它的方式。 include? 只是告诉您该内容是否在 HTML 中的任何位置,它不会告诉您该内容是否被客户端代码和/或 CSS 规则等可见或隐藏。该方法对于调试来说是一个糟糕的选择
【解决方案2】:

您似乎完全误解了等待函数的目的。我这么说是因为你说

我的主要问题是 wait_while_present 似乎不会继续,如果 该元素不存在。

行为是设计的

而 `.present?'方法返回 true 或 false(这是您对任何用 ruby​​ 编写的以问号结尾的方法所期望的),这不是各种“等待”函数的工作方式。同样,假设它们确实返回 true 或 false 也不合理,因为它们的方法名称不以问号结尾。

这些等待函数的目的是等待一个指定的持续时间,以达到应该必须显现的预期条件(相反可能)然后继续,或者如果超时到期而条件未得到满足,则引发错误。你可以:

  • 等待元素出现
  • 等待元素出现
  • 等待条件变为真
  • 等到条件变为假

使用这些方法进行同步时的假设是:如果已超过超时,则脚本无法可靠地继续。因此,测试在该点失败。

对于几乎所有语言中的所有常见测试框架来说,引发错误(也称为异常)以使测试失败的业务几乎是标准行为。黄瓜、rspec、junit、minitest、nunit 等测试断言/期望的工作方式相同方式,如果被断言的事物失败,则会引发错误。框架“捕获”错误,将详细信息记录为测试结果的一部分,使该测试/场景失败,然后继续下一个。如果测试期间正在执行的任何方法失败,例如尝试单击不存在的按钮,则会引发错误并且测试失败。

对于您想要做的事情,要使用这些方法,您需要将等待调用包装在异常处理代码中,以便在该方法最终引发错误时“拯救”错误,或者编写您自己的等待方法在您希望等待一段时间以等待可能出现的条件,然后根据结果有条件地响应,而不是仅仅引发错误。

如果没有简单的方法可以知道客户端代码是否正忙于更新页面,等待响应服务呼叫,或等待用户输入。对此没有约定,也没有标准的方法来判断是哪种情况。如果您很幸运,或者您的程序员关心测试能力,那么他们可能会帮助您提供一些可以用来确定情况的东西。例如,许多站点都有一个微调器图标,每当客户端代码忙于更新页面或从服务器获取数据时,它们就会显示该图标,然后您可以使用该元素的可见性来了解您的代码何时需要等待或何时可以安全继续.

请记住,这些方法可以使用参数来设置超时,并且在某些情况下还会出现错误消息。利用它可以大大提高您排除故障的能力。

例如,如果你有一个非常可靠的微调器,你可以做这样的事情来等待它出现最多 3 秒钟,然后最多等待 10 秒钟让它消失

b.element(:id => 'loading').wait_until_present(timeout=3)
b.element(:id => 'loading').wait_while_present(timeout=10)

如果我们希望在微调器从未出现的情况下不会失败,并且继续前进,但如果它在那里超过 10 秒就会失败,那么你可以像这样做一些基本的exception handling

begin
  b.element(:id => 'loading').wait_until_present(timeout=3)
rescue
  puts "waited three seconds without seeing spinner"
end
b.element(:id => 'loading').wait_while_present(timeout=10)

如果无法访问您正在测试的页面,其他任何人都很难就如何以一种仍然有效的稳健方式使您的代码与页面同步提出具体建议。

【讨论】:

  • 感谢您的详尽解释。这是我的第一次实习,我已经使用 Watir 大约一个月了,所以我一直在反复试验。我们有一个微调器图标(这就是 element(:id =&gt; "loading") 的意思),我在该元素上使用了 wait_while_present,但我猜微调器的显示不够一致,我无法以这种方式使用它。
  • 是的,这可能很难。你可以尝试做一个“等待微调器”辅助方法,它可能包括一些微小的等待,如 .1 或 .2 以留出时间让它出现,然后在存在时等待,然后可能是另一个微小的等待,然后是另一个等待现在..基本上是为了确保它不在身边。有点杂乱无章。手动与网站交互,并密切关注单击时之间的延迟,何时/如果您看到微调器,单击和微调器之间的延迟时间。如果它非常一致,请等到出现,然后在出现时等待。
  • 我鼓励你看看你是否可以与拥有该页面的程序员交谈,并询问他们在单击这些内容以展开内容时在幕后发生了什么,并询问你如何可能能够知道什么时候可以安全进行。也许他们会有一些想法。另外我猜你可能在学习 Ruby 的同时也在学习 Ruby?
  • 我编写了自己的if element.present? {Watir::wait.until { !(element.present?) } } 等方法,但这并没有多大帮助。是的,我正在学习 Ruby,而我的 CS 学位只有大约 8 个月。我认为管理层对我在这方面花费这么多时间感到恼火,但我觉得在整个测试自动化过程中处理加载微调器很重要。我可以访问前端代码(但我并不真正了解 JavaScript),所以我将与开发人员讨论它是如何工作的。
  • 如果你要使用微调器,那么他们在何时何地使用它必须非常可靠..
【解决方案3】:

我正在学习 ruby​​/watir/cucumber,我还想要一个等待,如果超时只会给我 False。 我想等待大约 10 秒钟,让弹出窗口出现与否,然后继续代码。例如,如果弹出窗口在不到一秒的时间内出现,则使用 sleep 10 是一种很大的浪费。作为初学者,我发现最好的方法是使用 If 块。我检查元素是否存在,如果不存在,我再等 3 秒。最多 9 秒。它解决了我的问题。

示例:我想检查是否出现带有“成功”一词的弹出窗口。如果是,则打印“通过”。如果 9 秒内没有出现,则打印“失败”。

if $browser.element(:text => "Success").exists? == False
sleep 3
end

if $browser.element(:text => "Success").exists? == False
sleep 3
end

if $browser.element(:text => "Success").exists? == False
sleep 3
end

if $browser.element(:text => "Success").exists? == False
puts "Failed"
end

if $browser.element(:text => "Success").exists?
puts "Passed"
end

test continues

也许这会有所帮助!

【讨论】:

    猜你喜欢
    • 2012-08-12
    • 2021-06-04
    • 2023-03-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-23
    • 2014-04-24
    相关资源
    最近更新 更多