How to avoid “StaleElementReferenceException” in Selenium?

前端 未结 16 2120
一向
一向 2020-11-22 12:12

I\'m implementing a lot of Selenium tests using Java. Sometimes, my tests fail due to a StaleElementReferenceException. Could you suggest some approaches to mak

相关标签:
16条回答
  • 2020-11-22 12:35

    There could be a potential problem that leads to the StaleElementReferenceException that no one mentioned so far (in regard to actions).

    I explain it in Javascript, but it's the same in Java.

    This won't work:

    let actions = driver.actions({ bridge: true })
    let a = await driver.findElement(By.css('#a'))
    await actions.click(a).perform() // this leads to a DOM change, #b will be removed and added again to the DOM.
    let b = await driver.findElement(By.css('#b'))
    await actions.click(b).perform()
    

    But instantiating the actions again will solve it:

    let actions = driver.actions({ bridge: true })
    let a = await driver.findElement(By.css('#a'))
    await actions.click(a).perform()  // this leads to a DOM change, #b will be removed and added again to the DOM.
    actions = driver.actions({ bridge: true }) // new
    let b = await driver.findElement(By.css('#b'))
    await actions.click(b).perform()
    
    0 讨论(0)
  • 2020-11-22 12:38

    Maybe it was added more recently, but other answers fail to mention Selenium's implicit wait feature, which does all the above for you, and is built into Selenium.

    driver.manage().timeouts().implicitlyWait(10,TimeUnit.SECONDS);

    This will retry findElement() calls until the element has been found, or for 10 seconds.

    Source - http://www.seleniumhq.org/docs/04_webdriver_advanced.jsp

    0 讨论(0)
  • 2020-11-22 12:44

    Generally this is due to the DOM being updated and you trying to access an updated/new element -- but the DOM's refreshed so it's an invalid reference you have..

    Get around this by first using an explicit wait on the element to ensure the update is complete, then grab a fresh reference to the element again.

    Here's some psuedo code to illustrate (Adapted from some C# code I use for EXACTLY this issue):

    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.
    

    Hope this helps!

    0 讨论(0)
  • 2020-11-22 12:46

    Kenny's solution is good, however it can be written in a more elegant way

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

    Or also:

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

    But anyway, best solution is to rely on Selenide library, it handles this kind of things and more. (instead of element references it handles proxies so you never have to deal with stale elements, which can be quite difficult). Selenide

    0 讨论(0)
  • 2020-11-22 12:48

    Kenny's solution is deprecated use this, i'm using actions class to double click but you can do anything.

    new FluentWait<>(driver).withTimeout(30, TimeUnit.SECONDS).pollingEvery(5, TimeUnit.SECONDS)
                        .ignoring(StaleElementReferenceException.class)
                        .until(new Function() {
    
                        @Override
                        public Object apply(Object arg0) {
                            WebElement e = driver.findelement(By.xpath(locatorKey));
                            Actions action = new Actions(driver);
                            action.moveToElement(e).doubleClick().perform();
                            return true;
                        }
                    });
    
    0 讨论(0)
  • 2020-11-22 12:50

    I was having this issue intermittently. Unbeknownst to me, BackboneJS was running on the page and replacing the element I was trying to click. My code looked like this.

    driver.findElement(By.id("checkoutLink")).click();
    

    Which is of course functionally the same as this.

    WebElement checkoutLink = driver.findElement(By.id("checkoutLink"));
    checkoutLink.click();
    

    What would occasionally happen was the javascript would replace the checkoutLink element in between finding and clicking it, ie.

    WebElement checkoutLink = driver.findElement(By.id("checkoutLink"));
    // javascript replaces checkoutLink
    checkoutLink.click();
    

    Which rightfully led to a StaleElementReferenceException when trying to click the link. I couldn't find any reliable way to tell WebDriver to wait until the javascript had finished running, so here's how I eventually solved it.

    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;
            }
        });
    

    This code will continually try to click the link, ignoring StaleElementReferenceExceptions until either the click succeeds or the timeout is reached. I like this solution because it saves you having to write any retry logic, and uses only the built-in constructs of WebDriver.

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