Selenium: How to avoid StaleElementReferenceException when looping through a set of elements?

前端 未结 2 896
野的像风
野的像风 2020-12-21 13:33

I have a page that contains a bunch of tables. I loop through the tables in the outer loop and then loop through each row in the table in the inner loop. It all works fine

相关标签:
2条回答
  • 2020-12-21 14:14

    Well after tearing my hair out for a day, I finally realized what was happening. It should have been obvious to me. When the "Next" button is clicked, it takes some time for the new page to load. By simply adding a delay, the new DOM is loaded and processing begins on it, not again on the previous one!

                driver.findElement(By.xpath(".//*[@class='PageRight']")).click();
            try {
                Thread.sleep(4000); //provide some time for the page to load before processing it
            } catch (InterruptedException ex) {
                Logger.getLogger(RealAuction.class.getName()).log(Level.SEVERE, null, ex);
            }
    

    Now it runs to completion with no StaleElementReferenceException.

    0 讨论(0)
  • 2020-12-21 14:20

    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.

    0 讨论(0)
提交回复
热议问题