【问题标题】:Robot Framework+Selenium: how to avoid "stale element" error (flaky test)?Robot Framework+Selenium:如何避免“陈旧元素”错误(片状测试)?
【发布时间】:2018-07-05 01:52:12
【问题描述】:

我正在使用 Robot Framework 和 Selenium 来测试具有语言选择器的网站。我需要能够选择一种语言,然后验证页面是否已实际更改为该语言。

由于选择新语言后,<html> 标记的 lang 属性中的值发生了变化,我决定使用它来验证语言是否已成功更改。但是我得到了不稳定的结果,因为我的测试有时会通过,有时不会。

这是我使用的关键字的定义:

CHANGE LANGUAGE  
  [Arguments]                        ${lang}
  Select From List By Value          ${LANGUAGE SWITCH}  ?hl=${lang}
  Wait Until Page Contains Element   css=html
  ${doc lang}                        Get Element Attribute  css=html@lang
  Should Be True                     '${doc lang}'=='${lang}'   timeout=15s

由于我必须多次执行此关键字(每种可用语言一个),我经常收到可怕的“陈旧元素”错误:| FAIL | stale element reference: element is not attached to the page document

我在这里阅读了this article 和其他一些问题,并了解如果在获取元素引用后更新它可能会发生这种情况。但是,我不确定应该如何修改关键字以避免出现此错误。

【问题讨论】:

  • 在找到元素并对其调用任何操作之前,您需要确保页面已加载。
  • 您是否在您发布的代码块或其他地方遇到异常?如果它在其他地方,您还应该发布该代码。我的猜测是还有另一种写法,这样你就可以避免异常。永远不要重用在语言切换之前获取的变量。我猜这就是问题所在而没有看到你所有的代码。
  • 哪个语句因陈旧元素异常而失败?我看不出您发布的任何人会如何做到这一点。
  • 我不确定如何检查哪个语句是引发异常的语句...这是一个关键字实现,而不是测试用例,所以我只收到一条消息,说明使用的测试此关键字失败。

标签: selenium robotframework


【解决方案1】:

使用每个人都提供的信息,我可能已经找到了一个潜在的修复方法(不确定它是否足够强大以不再抛出异常,但经过几次测试运行它们都通过了):我添加了一个“等待关键字成功”并将语言验证移至新关键字:

VALIDATE PAGE LANGUAGE
  [Arguments]                        ${lang}
  ${doc lang}                        Get Element Attribute  css=html@lang
  Should Be True                     '${doc lang}'=='${lang}'

CHANGE LANGUAGE  
  [Arguments]                        ${lang}
  Select From List By Value          ${LANGUAGE SWITCH}  ?hl=${lang}
  Wait For Condition                 return document.readyState=="complete"
  Wait Until Keyword Succeeds        5  5s  VALIDATE PAGE LANGUAGE  ${lang}

然后我调用这个“更改语言”关键字的次数与我需要测试的语言一样多。

我将此添加为答案而不是评论,以便我可以以更易读的方式显示代码。

【讨论】:

    【解决方案2】:

    为了在用户操作(例如单击链接或按钮)后等待页面准备好进行测试,我发现的算法似乎几乎是无懈可击的:

    1. 获取对 html 元素的引用
    2. 执行将导致页面更改的操作(例如:单击链接或按钮)
    3. 等待 html 元素过时 - 这表示刷新已开始
    4. 等待document.readyState成为"complete"

    第 4 步可能没有必要,但不会有什么坏处。

    这对我的团队非常有效。这仍然可能失败,因为您可能有一些在设置 document.readyState 之后运行的异步 javascript,但根本没有通用的解决方案。

    如果您的页面有一堆异步 javascript,那么您必须想出自己的方案才能知道页面何时最终准备好进行测试。例如,最后一个要完成的作业可以设置一个标志,或者您可以等到没有挂起的异步作业等。

    我不确定您是否可以使用机器人关键字执行上述操作,因为它依赖于 selenium staleness_of 条件。不过,它很容易在 python 中实现。

    这个解决方案的灵感来自这篇博文:How to get Selenium to wait for page load after a click

    如果您将我的page object library 用于机器人,这是作为上下文管理器内置的。

    【讨论】:

    • 感谢您的回答 :) 根据您上面的步骤,当我获得 ${LANGUAGE SWITCH} 定位器(获取对元素的引用)并从中选择值时,我知道我正在做 1 和 2列表(执行导致页面更改的操作)。我不确定如何等待元素过时。所以我尝试用Wait For Condition | return document.readyState=="complete" 替换我的“等待页面包含元素”,但结果是一样的(我得到了“陈旧元素”异常)。
    • @Floella: 不,你肯定在做 1 和 2。你必须得到一个参考(不只是等待它存在),然后去做 在更改语言之前。更改语言后,您需要等待 html 元素过时。我认为如果不在 python 中编写自定义关键字,就不可能等待元素过时。
    • 感谢您的澄清。然后我对如何实际获取元素的引用有点迷茫(我使用的是 Hi-Fi 的 Java SeleniumLibrary 版本)。我不确定这是否可行,但是有没有办法在刷新之前获取对 <html> 元素的引用,然后在刷新后再次获取它,然后比较它们是否改变了? (当我们在这里谈论“引用”时,我不禁想到指针或至少元素具有的某种唯一 ID,但也许我错了)。
    • @Floella:python 版本的库有一个Get webelement 关键字。看起来java版本没有类似的东西。您可能必须编写自己的关键字。是的,python 中的“引用”一词有点类似于指针。
    【解决方案3】:

    如突出显示的那样,Stale Element 错误通常意味着在元素检索和元素更改的相应操作之间。这通常是由于页面刷新造成的。

    因此,投资于稳健的等待方法非常重要。不要猜测您的页面/应用程序已完成加载,而是知道它已完成。这不仅可以防止Stale Element 错误,还可以加快您的测试,因为您无需等待。

    由于 Get Element Attribute ${DOCUMENT}@lang 导致过时元素错误,而 Select From List By Value ${LANGUAGE SWITCH} ?hl=${lang} 导致页面刷新,因此将 Wait Until Page Contains Element html 作为您的等待方法。

    由于<html> 标记始终存在并且第一个加载到 DOM 中,因此这不是等待的最佳标记。我会为加载的页面或该页面的最后一个元素建议一些独特的东西。虽然我必须强调,这仍然构成猜测页面已加载。

    最好投资于稳健的等待方法。特别是如果您的应用程序使用像 Angular、React 或 jQuery 这样的框架,那么您有几个 Javascript 标记可以帮助您解决这个问题。对于某些框架,甚至还有支持其特定标记的自定义机器人框架库。

    如果您的应用程序不使用框架,那么请与您的开发人员交谈并让他们为您开发此框架。最简单的将是一个可见的微调器,但返回 True 的 Javascript 函数也可以正常工作。

    【讨论】:

    • 感谢您的解释。至于页面加载,我认为 Webdriver 会自动等待它(或者这就是我从阅读这个github.com/robotframework/SeleniumLibrary/issues/107 得到的)。而且由于我需要检查的语言属性实际上是在 html 标记中,我不确定我是否理解为什么我需要等待整个页面完成加载...再次感谢 :)
    【解决方案4】:

    我创建了一个自定义关键字,当我想单击一个元素或执行和操作时,我会调用这个自定义关键字。此自定义关键字使用内置关键字“等待关键字成功”,它运行指定的关键字并在失败时重试。通过这个内置关键字,可以配置重试次数和在上次运行失败后再次尝试运行关键字之前等待的时间。

    此自定义关键字将调用另一个自定义关键字,其中将调用三个 SeleniumLibrary 关键字。第一个是 Waits until element locator is enabled 关键字,它将等到元素未被禁用或只读。一旦元素被启用,那么焦点将被带到我们将要执行操作的元素上。最后执行动作。所有这些预防措施都防止了我出现过时元素错误。

    HighLevelKeyword_Identify And Click Element
            [Arguments]    ${locator}
            Wait Until Keyword Succeeds    ${RETRY_ATTEMPTS}    ${RETRY_AFTER}    Identify And Click Element    ${locator}
    
    Identify And Click Element
        [Arguments]    ${locator}
        Wait Until Element Is Enabled    ${locator}
        Set Focus To Element    ${locator}
        Click Element    ${locator}
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-03-13
      • 2018-12-05
      • 1970-01-01
      • 2017-05-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多