The StaleElementReferenceException is thrown whenever you want to access an element reference which is not available anymore. This happens when the element is no longer attached to the DOM or if the page was updated.
The solution for this is just searching for the element again whenever this happens.
You could adapt all your tests or page objects. Or you write your own RobustWebDriver and RobustWebElement which refreshes the element if a SERE is thrown.
RobustWebDriver:
public class RobustWebDriver implements WebDriver {
private WebDriver originalWebDriver;
public RobustWebDriver(WebDriver webDriver) {
this.originalWebDriver = webDriver;
}
@Override
public void get(String url) {
this.originalWebDriver.get(url);
}
@Override
public String getCurrentUrl() {
return this.originalWebDriver.getCurrentUrl();
}
@Override
public String getTitle() {
return this.originalWebDriver.getTitle();
}
@Override
public List<WebElement> findElements(By by) {
List<WebElement> elements = new ArrayList<>();
for (WebElement element : this.originalWebDriver.findElements(by)) {
elements.add(new RobustWebElement(element, by, this));
}
return elements;
}
@Override
public WebElement findElement(By by) {
return new RobustWebElement(this.originalWebDriver.findElement(by), by, this);
}
@Override
public String getPageSource() {
return this.originalWebDriver.getPageSource();
}
@Override
public void close() {
this.originalWebDriver.close();
}
@Override
public void quit() {
this.originalWebDriver.quit();
}
@Override
public Set<String> getWindowHandles() {
return this.originalWebDriver.getWindowHandles();
}
@Override
public String getWindowHandle() {
return this.originalWebDriver.getWindowHandle();
}
@Override
public TargetLocator switchTo() {
return this.originalWebDriver.switchTo();
}
@Override
public Navigation navigate() {
return this.originalWebDriver.navigate();
}
@Override
public Options manage() {
return this.originalWebDriver.manage();
}
}
RobustWebElement:
public class RobustWebElement implements WebElement {
private WebElement originalElement;
private RobustWebDriver driver;
private By by;
private static final int MAX_RETRIES = 10;
public RobustWebElement(WebElement element, By by, RobustWebDriver driver) {
this.originalElement = element;
this.by = by;
this.driver = driver;
}
@Override
public void click() {
int retries = 0;
while (retries < MAX_RETRIES) {
try {
this.originalElement.click();
return;
} catch (StaleElementReferenceException ex) {
refreshElement();
}
retries++;
}
throw new StaleElementReferenceException(
String.format("Element is still stale after %s retries.", MAX_RETRIES));
}
@Override
public void sendKeys(CharSequence... keysToSend) {
int retries = 0;
while (retries < MAX_RETRIES) {
try {
this.originalElement.sendKeys(keysToSend);
return;
} catch (StaleElementReferenceException ex) {
refreshElement();
}
retries++;
}
throw new StaleElementReferenceException(
String.format("Element is still stale after %s retries.", MAX_RETRIES));
}
// TODO add other unimplemented methods with similar logic.
private void refreshElement() {
this.originalElement = driver.findElement(by);
}
And then you just need to wrap your WebDriver into the RobustWebDriver and you are ready to go:
WebDriver driver = new RobustWebDriver(new ChromeDriver());
EDIT:
Of course you need to take care of scrolling up and down by yourself.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…