Maalamaal
Maalamaal

Reputation: 973

Selenium WebDriver StaleElementReferenceException

I get this error when running my tests: org.openqa.selenium.StaleElementReferenceException: Element is no longer attached to the DOM

any idea on how to solve the above exception? this happen in my grid which has a ref Xpath expression which is dynamic

Upvotes: 26

Views: 46489

Answers (11)

Armando
Armando

Reputation: 622

I ran across this same problem and could not find any solutions. Came up with a solution and posting it here, hope this helps someone with the same problem. I created a class to handle stale elements depending on their type, cssselector, id, etc and simply call it like I would any other page object.

public void StaleElementHandleByID (String elementID)
{
    int count = 0;
    boolean clicked = false;
    while (count < 4 && !clicked)
    {
        try 
        {
            WebElement yourSlipperyElement= driver.findElement(By.id(elementID));
            yourSlipperyElement.click(); 
            clicked = true;
        } 
        catch (StaleElementReferenceException e)
        {
            e.toString();
            System.out.println("Trying to recover from a stale element :" + e.getMessage());
            count = count+1;
        }
    }
}

I'd recommend only using this on elements you know cause problems for WebDriver.

Upvotes: 33

Watth
Watth

Reputation: 446

@netz75 : Thank you. Had this problem when a click redirects to second page. This worked for me:

 //.. (first page asserts)
 //...
 element.Click();   
 Thread.Sleep(200);
 //.. (second page asserts)
 //...

Upvotes: 0

Nash N
Nash N

Reputation: 359

public static Boolean executeElementSendKeys
(WebDriver driver, WebElement element, String sInputParameters) throws Exception {
    return (Boolean) executeElementMaster
            (driver, element, "sendKeys", sInputParameters, 30, true);
}

public static Boolean executeElementClear
(WebDriver driver, WebElement element) throws Exception {
    return (Boolean) executeElementMaster (driver, element, "clear", "", 30, true);
}

public static String executeElementGetText
(WebDriver driver, WebElement element) throws Exception {
    return (String) executeElementMaster (driver, element, "getText", "", 30, true);
}

public static Boolean executeElementClick
(WebDriver driver, WebElement element) throws Exception {
    return (Boolean) executeElementMaster (driver, element, "click", "", 30, true);
}

public static boolean executeElementIsDisplayed
(WebDriver driver, WebElement element) throws Exception {
    return (Boolean) executeElementMaster (driver, element, "isDisplayed", "", 30, true);
}

public static String executeElementGetAttribute
(WebDriver driver, WebElement element, String sInputParameters) throws Exception {
    return (String) executeElementMaster
            (driver, element, "getAttribute", sInputParameters, 30, true);
}

// And below is the master method that handles the StaleElementReferenceException and other exceptions.

// In the catch section, replace (Exception e) with (StaleElementReferenceException e) if you want this method to retry actions (like click, sendkeys etc.) for only StaleElementReferenceException and not other exceptions.

private static Object executeElementMaster(WebDriver driver, WebElement element, String sExecuteAction, String sInputParametersOptional, int MaxTimeToWait,
        boolean bExpectedElementState) throws Exception {
    try {
        // Local variable declaration
        String sElementString = "";
        String sElementXpath = "";
        Object ReturnValue = "";
        int Index = 0;
        boolean bCurrentElementState = true;
        boolean bWebDriverWaitUntilElementClickableFlag = false;

        System.out.println("**** Execute method '" + sExecuteAction + "' on '" + sElementString + "' - Expected : '" + bExpectedElementState + "'");
        System.out.println("**** MaxTimeToWait ='" + MaxTimeToWait + "' seconds");

        // Set browser timeout to 1 second. Will be reset to default later
        driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS);

        // Keep trying until 'MaxTimeToWait' is reached 
        for (int i = 0; i < MaxTimeToWait; i++) {
            try {
                // Get element xPath - and find element again
                if (element != null && i < 2 && sElementString == "") {
                    sElementString = (element).toString();
                    if (sElementString.contains("xpath: ")) {
                        // Retrieve xPath from element, if available
                        Index = sElementString.indexOf("xpath: ");
                        sElementXpath = sElementString.substring(Index + 7, sElementString.length());
                    }
                }

                // Find Element again
                if (sElementXpath != "" && i > 0) {
                    element = driver.findElement(By.xpath(sElementXpath));
                }

                // Execute the action requested
                switch (sExecuteAction) {
                    case ("isDisplayed"):
                        // Check if element is displayed and save in bCurrentElementState variable
                        ReturnValue = element.isDisplayed();
                        bCurrentElementState = (Boolean) ReturnValue;
                        bWebDriverWaitUntilElementClickableFlag = true;
                        break;
                    case ("getText"):
                        ReturnValue = element.getText();
                        bCurrentElementState = true;
                        bWebDriverWaitUntilElementClickableFlag = false;
                        break;
                    case ("sendKeys"):
                        // Scroll element into view before performing any action

                        element.sendKeys(sInputParametersOptional);
                        ReturnValue = true;
                        bCurrentElementState = true;
                        bWebDriverWaitUntilElementClickableFlag = false;
                        break;
                    case ("clear"):
                        // Scroll element into view before performing any action

                        element.clear();
                        ReturnValue = true;
                        bCurrentElementState = true;
                        bWebDriverWaitUntilElementClickableFlag = false;
                        break;
                    case ("click"):
                        // Scroll element into view before performing any action

                        element.click();
                        ReturnValue = true;
                        bCurrentElementState = true;
                        bWebDriverWaitUntilElementClickableFlag = false;
                        break;
                    default:
                        ReturnValue = element.getAttribute(sInputParametersOptional);
                        bCurrentElementState = true;
                        break;
                }
            } catch (Exception e) {
                Thread.sleep(500);
                bCurrentElementState = false;
                ReturnValue = false;
            }
            if (bCurrentElementState == bExpectedElementState) {
                // If element's actual and expected states match, log result and return value
                System.out.println("**** PASSED: Execute method '" + sExecuteAction + "' on '" + sElementString + "' - Returned '" + ReturnValue + "' ****   \n"
                        + "Actual element status: '" + bCurrentElementState + "'  Expected element status: '" + bExpectedElementState + "'");
                break;
            } else {
                // If element's actual and expected states do NOT match, loop until they match or timeout is reached
                Thread.sleep(500);
            }
        }
        // Reset browser timeout to default
        driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
        // Return value before exiting
        if (bCurrentElementState != bExpectedElementState) {
            // If element's actual and expected states do NOT match, log result and return value
            System.out.println("**** FAILED: Execute method '" + sExecuteAction + "' on '" + sElementString + "' - Returned '" + ReturnValue + "' ****   \n"
                    + "Actual element status: '" + bCurrentElementState + "'  Expected element status: '" + bExpectedElementState + "'");
            if (sExecuteAction.equalsIgnoreCase("findElement")) {
                ReturnValue = null;
            }
        }

        return ReturnValue;
    } catch (Exception e) {
        System.out.println("Exception in executeElementMaster - " + e.getMessage());
        throw (e);
    }
}

Upvotes: 1

Pavan
Pavan

Reputation: 1247

We get around this issue by doing something called WebdriverWrapper and WebElementWrapper.

What these wrappers do is handle the StaleElementException within and then use the locator to re-evaluate and get the new WebElement object. This way, you need to spread the code handling the exception all over your codebase and localize it to one class.

I will look into open sourcing just these couple of classes soon and add a link here if you folk are interested.

Upvotes: 8

Kermit_ice_tea
Kermit_ice_tea

Reputation: 518

In this case, the tests are looking for an element that is not yet loaded, or has been refreshed. As a result, the StaleElementException. A simple solution would be to add a fluentWait.

Upvotes: 0

djangofan
djangofan

Reputation: 29669

I used a FluentWait and also the ExpectedCondition apply override: https://gist.github.com/djangofan/5112655 . This one handles the exception inside the finalized block, unlike how other people answer this, and allows the successive tries to be wrapped in that block. I think this is more elegant.

public static void clickByLocator( final By locator ) {
  final long startTime = System.currentTimeMillis();
  driver.manage().timeouts().implicitlyWait( 5, TimeUnit.SECONDS );
  Wait<WebDriver> wait = new FluentWait<WebDriver>( driver )
        .withTimeout(90000, TimeUnit.MILLISECONDS)
        .pollingEvery(5500, TimeUnit.MILLISECONDS);
        //.ignoring( StaleElementReferenceException.class );        
  wait.until( new ExpectedCondition<Boolean>() { 
    @Override 
    public Boolean apply( WebDriver webDriver ) {
      try {
        webDriver.findElement( locator ).click();
        return true;
      } catch ( StaleElementReferenceException e ) {                      // try again
        return false;
      }     
    } 
  } );      
  driver.manage().timeouts().implicitlyWait( DEFAULT_IMPLICIT_WAIT, TimeUnit.SECONDS );
  long endTime   = System.currentTimeMillis();
  long totalTime = endTime - startTime;
  log("Finished click after waiting for " + totalTime + " milliseconds.");
}

Upvotes: 1

ram_c
ram_c

Reputation: 87

You might be trying to get any of element properties after clicking an element.

I had the same issue, I was trying to getText() of button after it was clicked. In my case, once button is clicked, new window comes.

Upvotes: 1

netz75
netz75

Reputation: 57

a quick & dirty solution:

el.click()

time.sleep(1)

then continue to parse in iteration way

Upvotes: 0

WoF_Angel
WoF_Angel

Reputation: 2581

I made some changes to be more flexible:

   delegate void StaleFunction(IWebElement elt);
        private static void StaleElementHandleByID(By by, StaleFunction func )
        {
            int count = 0;
            while (count < 4)
            {
                try
                {
                    IWebElement yourSlipperyElement = Driver.FindElement(by);
                    func(yourSlipperyElement);
                    count = count + 4;
                }
                catch (StaleElementReferenceException e)
                {
                    count = count + 1;
                }

            }
        }

 StaleElementHandleByID(By.Id("identDdl"),
                    delegate(IWebElement elt)
                {
                    SelectElement select = new SelectElement(elt);
                    select.SelectByText(tosave.ItemEquipamentoCem.CodigoCne.ToString());
                });

Upvotes: 0

divjscr
divjscr

Reputation: 61

It also encountered this issue, It looks very obvious the modal panel loading falls into a race condition and keeps waiting till time out.

I have tried many times, and found the solution is to hold the modal panel loading till it can be exactly found by webDriver and at the same time keep refresh the webDriver instance , then try to find WebElements within the modal panel.

So the solution is like follows: e.g. MyModalPanel is your ModalPanel Id, then do the following

page.openModalPanel();
Assert.assertTrue(page.waitTillDisplay( "MyModalPanelContentDiv"), Wait.MODAL_PANEL));
page.fillInFormInModalpanel(formObj);

and the waitTillDisplay code can be found on WebDriver website, I will just paste my code here for your reference:

public Boolean waitTillDisplay(final String id, int waitSeconds){

    WebDriverWait wait = new WebDriverWait(driver, waitSeconds);
        Boolean displayed = wait.until(new ExpectedCondition<Boolean>() {
              public Boolean apply(WebDriver driver) {
                  return driver.findElement(By.id(id)).isDisplayed();
              }

        });
        return displayed;

}

Upvotes: 2

pnewhook
pnewhook

Reputation: 4068

That exception is thrown when you try to use a method of a WebElement that is not longer on the page. If your grid is dynamically loading data and you refresh the grid, any references to elements on that grid would be 'stale'. Double check that the element you're trying to reference is on the page in your tests, and you may need to re-instantiate the object.

Upvotes: 4

Related Questions