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)));
}
我 运行 在 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)));
}