我正在尝试学习PageFactory模型。我理解这样一个事实,即当我们做一个initElements
,Web元素被定位。例如,我单击一个 webelement,因此 DOM 中的其他 Web 元素之一发生了变化。现在,显然我会在这里得到一个StaleElementReferenceException
。我将如何解决此问题?
我是否应该再次找到特定的WebElement,知道DOM中WebElement的属性可能会发生变化? 还是有另一种方法可以解决这个问题?
StaleElementReferenceException
StaleElementReferenceException 扩展了 WebDriverException,并指示元素的先前引用现在已过时,并且元素引用不再存在于页面的 DOM 上。
常见原因
- 面临
StaleElementReferenceException
的常见原因如下:- 该元素已被完全删除。
- 该元素不再附加到 DOM。
- 元素所属的网页已刷新。
- (以前的(元素已被 JavaScript 或 AjaxCall 删除,并替换为具有相同
ID
或其他属性的(新(元素。
- 解决方案:如果一个(旧的(元素已被新的相同元素替换,简单的策略是使用
findElement()
或findElements
再次查找该元素。
回答您的疑问
-
当我们执行 initElements 时,WebElements 位于 :当您调用
initElements()
方法时,该页面的所有 WebElements 都将被初始化。例如
无论何时何地从自动化脚本调用此代码,此代码行都将初始化LoginPageNew login_page = PageFactory.initElements(driver, LoginPageNew.class);
LoginPageNew.class
范围内定义的所有静态 WebElements。 -
我点击了一个webelement,因此DOM中的其他Web元素之一发生了变化:这几乎是可能的。
- 例如,通常调用
<input>
标签上的click()
不会触发HTML DOM上任何Web元素的任何更改。 - 在
<button>
标签或<a>
标签上调用click()
可以调用JavaScript或Ajax,后者反过来可能会删除元素或可以用具有相同ID
或其他属性的(新(元素替换(以前的(元素。
- 例如,通常调用
结论
因此,如果 WebDriver 抛出 StaleElementReferenceException,这意味着即使该元素仍然存在,引用也会丢失。我们应该丢弃我们拥有的当前引用,并在 WebElement 附加到 DOM 时再次找到它来替换它。这意味着您必须通过initElements()
方法再次重新初始化类,该方法反过来重新初始化该页面中定义的所有WebElements。
溶液
如果旧元素已替换为新的相同元素,则简单的策略是将WebDriverWait与ExpectConditions一起调用以查找该元素。
您可以在以下位置找到相关的详细讨论:
- 如何在PageObjectModel的PageFactory中添加显式等待?
引用
以下是本次讨论的参考资料:
- 过时元素引用异常
- 类 StaleElementReferenceException
- Selenium:如何判断 RemoteWebDriver.findElements(By( 是否可以抛出 StaleElementReferenceException?
这是PageFactory实现的已知问题。
如果您不幸导致元素在找到元素和单击元素之间的瞬间变得过时,您将收到此错误。 不幸的是,如果元素已过时并且引发异常,PageFactory 代码不会尝试再次查找该元素。
我会将其归类为 PageFactory 的错误,如果元素变得过时,它应该自动重新找到该元素(除非使用@CacheLookup注释(。
召回 initElements 的建议不会解决任何问题,您只需要初始化元素一次,因为这会将 Java 代理类绑定到相关元素。 页面工厂实现应该消除StaleElementReferenceExceptions的可能性(因此为什么这是一个错误(
Stale element exception
该元素不再附加到DOM
。 该元素已被完全删除。
发生这种情况时,您将代码包装在try catch block
然后您可以根据需要多次循环和重试,直到成功。
public void waitForElementPresent(final By by, int timeout){
WebDriverWait wait = (WebDriverWait)new WebDriverWait(driver,timeout)
.ignoring(StaleElementReferenceException.class);
wait.until(new ExpectedCondition<Boolean>(){
@Override
public Boolean apply(WebDriver webDriver) {
WebElement element = webDriver.findElement(by);
return element != null && element.isDisplayed();
}
});
}