StaleElementReferenceException 即使我使用`FluentWait<>`

StaleElementReferenceException even when I use `FluentWait<>`

我 运行 在 Ubunto 上进行 web E2E 测试(使用 cucumber、junit、selenium webDriver)。

我的测试在使用 remoteWebDriver 时 偶尔 失败(使用 local-webDriver 从不失败)

Then banner image in preview html should be "p_1047_o_9075_banner_1423040308.png"


Failure 
org.openqa.selenium.StaleElementReferenceException: stale element reference: element is not attached to the page document

当我重构我的代码时,我以为我已经绕过了这个异常:

    public String getSrcAfterWait(final By by) {
        WebElement webElement = getElementAfterWaitForDisplay2(by);
        String currentSrc = null;
        if (webElement != null) {
            currentSrc = webElement.getAttribute("src");
        }
        return currentSrc;
    }


 public WebElement getElementAfterWaitForDisplay2(final By by) {
        Wait<WebDriver> wait = new FluentWait<>(driver)
                .withTimeout(30, TimeUnit.SECONDS)
                .pollingEvery(5, TimeUnit.SECONDS)
                .ignoring(NoSuchElementException.class, StaleElementReferenceException.class);

        return wait.until(ExpectedConditions.presenceOfElementLocated(by));
    }

我怎样才能进一步稳定这个测试?

更新

我正在尝试 mentallurg 解决方案。但这结构合理吗?

  public String getButtonTextAfterWait(final By by) {
        String currentText;
        try {
            currentText = tryToGetText(by);

        } catch (StaleElementReferenceException ex)
        {
            currentText = tryToGetText(by);
        }
        return currentText;
    }

如果您的页面是动态的,可能会发生以下情况。

场景一。 在方法 "getSrcAfterWait" 中,您使用 "getElementAfterWaitForDisplay2" 定位了元素。但是在调用 webElement.getAttribute("src") 之前,页面上的元素发生了变化。当您调用 webElement.getAttribute("src") 时,"webElement" 指向一个已经过时(不再存在)的元素。这会导致 StaleElementReferenceException。

场景二。 也许你有某种动画。这样就可以定期多次创建元素的新实例。 "getElementAfterWaitForDisplay2" 总能找到符合 "By" 条件的元素,但有时它是页面上较新的对象。在您找到该元素之后和调用 webElement.getAttribute("src") 之前,页面上(在 DOM 中)创建了一个新实例。这样您定位的变量就可以引用已经过时的实例。

两种情况的简单解决方案:捕获异常并重新定位对象。

更复杂的解决方案,但更通用:使用代理对象并实施异常处理和定位。

@EladBenda:不是真的。你应该在一个循环中这样做。但是您不需要编写循环代码。请改用 FluentWait。您只需要将您的方法 "getButtonTextAfterWait" 转换为 ExpectedCondition 或 Predicate 或 Function 并将其传递给 FluentWait。您不需要显式捕获异常。你可以通过 ...ignoring(...Exception.class) 来做到这一点,就像你在其他地方所做的那样。它可以如下所示:

Wait wait = new FluentWait(driver)
  .withTimeout(60, SECONDS)
  .pollingEvery(5, SECONDS)
  .ignoring(NoSuchElementException.class, StaleElementReferenceException.class);

String text = wait.until(new Function() {
  public String apply(WebDriver driver) {
    WebElement element = driver.findElement(By.id("..."));
    return element.getText(); // or webElement.getAttribute("src")  etc.
  }
});
public void waitForElementBeDisplay(WebElement element) {


    Wait wait = new FluentWait(driver).withTimeout(Duration.ofSeconds(30)).ignoring(StaleElementReferenceException.class).pollingEvery(Duration.ofSeconds(2));
    wait.until(ExpectedConditions.refreshed(ExpectedConditions.visibilityOf(element)));

}