How to avoid “StaleElementReferenceException” in Selenium?

前端 未结 16 2122
一向
一向 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:51

    The problem is by the time you pass the element from Javascript to Java back to Javascript it can have left the DOM.
    Try doing the whole thing in Javascript:

    driver.executeScript("document.querySelector('#my_id').click()") 
    
    0 讨论(0)
  • 2020-11-22 12:54

    This can happen if a DOM operation happening on the page is temporarily causing the element to be inaccessible. To allow for those cases, you can try to access the element several times in a loop before finally throwing an exception.

    Try this excellent solution from darrelgrainger.blogspot.com:

    public boolean retryingFindClick(By by) {
        boolean result = false;
        int attempts = 0;
        while(attempts < 2) {
            try {
                driver.findElement(by).click();
                result = true;
                break;
            } catch(StaleElementException e) {
            }
            attempts++;
        }
        return result;
    }
    
    0 讨论(0)
  • 2020-11-22 12:55

    I've found solution here. In my case element becomes inaccessible in case of leaving current window, tab or page and coming back again.

    .ignoring(StaleElement...), .refreshed(...) and elementToBeClicable(...) did not help and I was getting exception on act.doubleClick(element).build().perform(); string.

    Using function in my main test class:

    openForm(someXpath);
    

    My BaseTest function:

    int defaultTime = 15;
    
    boolean openForm(String myXpath) throws Exception {
        int count = 0;
        boolean clicked = false;
        while (count < 4 || !clicked) {
            try {
                WebElement element = getWebElClickable(myXpath,defaultTime);
                act.doubleClick(element).build().perform();
                clicked = true;
                print("Element have been clicked!");
                break;
            } catch (StaleElementReferenceException sere) {
                sere.toString();
                print("Trying to recover from: "+sere.getMessage());
                count=count+1;
            }
        }
    

    My BaseClass function:

    protected WebElement getWebElClickable(String xpath, int waitSeconds) {
            wait = new WebDriverWait(driver, waitSeconds);
            return wait.ignoring(StaleElementReferenceException.class).until(
                    ExpectedConditions.refreshed(ExpectedConditions.elementToBeClickable(By.xpath(xpath))));
        }
    
    0 讨论(0)
  • 2020-11-22 12:57

    The reason why the StaleElementReferenceException occurs has been laid out already: updates to the DOM between finding and doing something with the element.

    For the click-Problem I've recently used a solution like this:

    public void clickOn(By locator, WebDriver driver, int timeout)
    {
        final WebDriverWait wait = new WebDriverWait(driver, timeout);
        wait.until(ExpectedConditions.refreshed(
            ExpectedConditions.elementToBeClickable(locator)));
        driver.findElement(locator).click();
    }
    

    The crucial part is the "chaining" of Selenium's own ExpectedConditions via the ExpectedConditions.refreshed(). This actually waits and checks if the element in question has been refreshed during the specified timeout and additionally waits for the element to become clickable.

    Have a look at the documentation for the refreshed method.

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

    In my project I introduced a notion of StableWebElement. It is a wrapper for WebElement which is able to detect if element is Stale and find a new reference to the original element. I have added a helper methods to locating elements which return StableWebElement instead of WebElement and the problem with StaleElementReference disappeared.

    public static IStableWebElement FindStableElement(this ISearchContext context, By by)
    {
        var element = context.FindElement(by);
        return new StableWebElement(context, element, by, SearchApproachType.First);
    } 
    

    The code in C# is available on my project's page but it could be easily ported to java https://github.com/cezarypiatek/Tellurium/blob/master/Src/MvcPages/SeleniumUtils/StableWebElement.cs

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

    Clean findByAndroidId method that gracefully handles StaleElementReference.

    This is heavily based off of jspcal's answer but I had to modify that answer to get it working cleanly with our setup and so I wanted to add it here in case it's helpful to others. If this answer helped you, please go upvote jspcal's answer.

    // This loops gracefully handles StateElementReference errors and retries up to 10 times. These can occur when an element, like a modal or notification, is no longer available.
    export async function findByAndroidId( id, { assert = wd.asserters.isDisplayed, timeout = 10000, interval = 100 } = {} ) {
      MAX_ATTEMPTS = 10;
      let attempt = 0;
    
      while( attempt < MAX_ATTEMPTS ) {
        try {
          return await this.waitForElementById( `android:id/${ id }`, assert, timeout, interval );
        }
        catch ( error ) {
          if ( error.message.includes( "StaleElementReference" ) )
            attempt++;
          else
            throw error; // Re-throws the error so the test fails as normal if the assertion fails.
        }
      }
    }
    
    0 讨论(0)
提交回复
热议问题