如何避免Selenium中的"StaleElementReferenceException"?

hoa*_*yen 67 java selenium-webdriver

我正在使用Java实现许多Selenium测试.有时,我的测试由于a而失败StaleElementReferenceException.你能建议一些让测试更稳定的方法吗?

jsp*_*cal 68

如果页面上发生的DOM操作暂时导致元素不可访问,则会发生这种情况.为了允许这些情况,您可以在最终抛出异常之前尝试在循环中多次访问该元素.

从darrelgrainger.blogspot.com尝试这个出色的解决方案:

public boolean retryingFindClick(By by) {
    boolean result = false;
    int attempts = 0;
    while(attempts < 2) {
        try {
            driver.findElement(by).click();
            result = true;
            break;
        } catch(StaleElementException e) {
        }
        attempts++;
    }
    return result;
}
Run Code Online (Sandbox Code Playgroud)

  • @Vdex ChromeDriver 的版本与此问题无关。 (12认同)
  • 也可以通过使用不同的元素引用来修复它。 (2认同)

Ken*_*nny 61

我间歇性地遇到这个问题.我不知道,BackboneJS正在页面上运行并替换我试图点击的元素.我的代码看起来像这样.

driver.findElement(By.id("checkoutLink")).click();
Run Code Online (Sandbox Code Playgroud)

这当然在功能上与此相同.

WebElement checkoutLink = driver.findElement(By.id("checkoutLink"));
checkoutLink.click();
Run Code Online (Sandbox Code Playgroud)

偶尔会发生的是javascript将在查找和单击它之间替换checkoutLink元素,即.

WebElement checkoutLink = driver.findElement(By.id("checkoutLink"));
// javascript replaces checkoutLink
checkoutLink.click();
Run Code Online (Sandbox Code Playgroud)

在尝试单击链接时,这正确地导致了StaleElementReferenceException.我找不到任何可靠的方法告诉WebDriver等到javascript运行完毕,所以这就是我最终如何解决它.

new WebDriverWait(driver, timeout)
    .ignoring(StaleElementReferenceException.class)
    .until(new Predicate<WebDriver>() {
        @Override
        public boolean apply(@Nullable WebDriver driver) {
            driver.findElement(By.id("checkoutLink")).click();
            return true;
        }
    });
Run Code Online (Sandbox Code Playgroud)

此代码将不断尝试单击该链接,忽略StaleElementReferenceExceptions,直到单击成功或达到超时.我喜欢这个解决方案,因为它可以节省您编写任何重试逻辑,并且只使用WebDriver的内置结构.

  • 这个答案应该被接受了! (4认同)

Jim*_*mes 15

通常这是由于DOM正在更新并且您尝试访问更新的/新元素 - 但DOM已刷新,因此它是您的无效引用.

首先使用元素上的显式等待来确保更新完成,然后再次获取对元素的新引用.

这里有一些伪代码来说明(改编自我使用的一些C#代码这个问题):

WebDriverWait wait = new WebDriverWait(browser, TimeSpan.FromSeconds(10));
IWebElement aRow = browser.FindElement(By.XPath(SOME XPATH HERE);
IWebElement editLink = aRow.FindElement(By.LinkText("Edit"));

//this Click causes an AJAX call
editLink.Click();

//must first wait for the call to complete
wait.Until(ExpectedConditions.ElementExists(By.XPath(SOME XPATH HERE));

//you've lost the reference to the row; you must grab it again.
aRow = browser.FindElement(By.XPath(SOME XPATH HERE);

//now proceed with asserts or other actions.
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助!


coc*_*llo 11

肯尼的解决方案很好,但它可以用更优雅的方式编写

new WebDriverWait(driver, timeout)
        .ignoring(StaleElementReferenceException.class)
        .until((WebDriver d) -> {
            d.findElement(By.id("checkoutLink")).click();
            return true;
        });
Run Code Online (Sandbox Code Playgroud)

或者:

new WebDriverWait(driver, timeout).ignoring(StaleElementReferenceException.class).until(ExpectedConditions.elementToBeClickable(By.id("checkoutLink")));
driver.findElement(By.id("checkoutLink")).click();
Run Code Online (Sandbox Code Playgroud)

但无论如何,最好的解决方案是依靠Selenide库,它处理这类事情等等.(而不是元素引用它处理代理,所以你永远不必处理过时的元素,这可能是非常困难的).

  • 人们应该知道OP选择的答案可以追溯到2012年。过去7年里很多事情都发生了变化。这个答案对于2019年来说更正确。 (2认同)

Chr*_*n.D 7

StaleElementReferenceException已经确定了发生这种情况的原因:在查找和使用元素执行某些操作之间更新DOM.

对于点击问题,我最近使用了这样的解决方案:

public void clickOn(By locator, WebDriver driver, int timeout)
{
    final WebDriverWait wait = new WebDriverWait(driver, timeout);
    wait.until(ExpectedConditions.refreshed(
        ExpectedConditions.elementToBeClickable(locator)));
    driver.findElement(locator).click();
}
Run Code Online (Sandbox Code Playgroud)

最关键的部分是硒自己的"链" ExpectedConditions通过ExpectedConditions.refreshed().这实际上等待并检查在指定的超时期间是否刷新了有问题的元素,并另外等待元素变为可点击.

查看刷新方法文档.


cez*_*tek 5

在我的项目中,我引入了 StableWebElement 的概念。它是 WebElement 的包装器,能够检测元素是否过时并找到对原始元素的新引用。我添加了一个辅助方法来定位返回 StableWebElement 而不是 WebElement 的元素,并且 StaleElementReference 的问题消失了。

public static IStableWebElement FindStableElement(this ISearchContext context, By by)
{
    var element = context.FindElement(by);
    return new StableWebElement(context, element, by, SearchApproachType.First);
} 
Run Code Online (Sandbox Code Playgroud)

C# 代码可在我的项目页面上找到,但可以轻松移植到 java https://github.com/cezarypiatek/Tellurium/blob/master/Src/MvcPages/SeleniumUtils/StableWebElement.cs