4

I keep running into the Stale Element Error in Selenium. From what I have researched, this error takes place when you navigate away from a page or the element has been changed. I keep hitting this error when I navigate through a list of web elements to try and identify one of them by a value. It always fails on the if statement below when I try and compare the name. Does anyone know what my issue is? On a larger question, is there a good fix for stale element error? All I have seen online is to catch the error and loop through it again. Is there a better way than this?

    int attempts = 0;
    while(attempts < 2){
    try{
    java.util.List<WebElement> elements = driver.findElements(By.tagName("span"));
    for(WebElement element: elements){
        if(element.getText().equals(elementName)){
          return element;
        }
    }
    }catch (org.openqa.selenium.StaleElementReferenceException e){
                e.printStackTrace();
    }
        attempts++;
    }
    return null;
5
  • 1
    One thing I do to avoid scenarios like what you posted above, is keep your parent page from changing as much as possible. What I mean by this, is instead of navigating from link to link through a site for your test, open any links you can in a new window; switch to that window; and when you are done close it and return to the parent. Commented Mar 4, 2014 at 20:38
  • 1
    @Ben I think it is a bad advice as your functional tests should mimic the users behaviour as closely as possible. You should really deal with the problem, not try to find a way around it. The wait class can help you overcome the staleness problem with ease. Commented Mar 4, 2014 at 22:32
  • 1
    @ErkiM I agree with you on that aspect, but I disagree if your test isn't made to mimic users; such as web scraping or obtaining information. The larger amount of site navigation, the larger possibility of having site navigation or network related issues. My intent on the post was to show that even if a child page fails, the parent can persist. Commented Mar 4, 2014 at 23:18
  • @Ben, my comment was indeed on functional testing of websites. Anyway, there are definitely tools that outrun webdriver in almost every aspect when speaking of web scraping, starting with curl or wget in combination with something like sed or egrep. But all in all, point taken. Commented Mar 5, 2014 at 8:23
  • Thanks to the both of you for your help. My issue was that I was making changes in a profile that pops up in a modal window. So every time I ran a test, I would open the modal window, make a change, save the profile and close the modal. Then I would re-open and find the checkbox or whatever it was to make an assertion if it had changed or not. As you can imagine, this caused the error. So I've decided to just make assertion right after I make the change and this fixes the error. Thanks! Commented Mar 6, 2014 at 20:19

1 Answer 1

2

First of all you should write better locators. What you are doing is that you are querying all the <span> elements into list of webelements and then in your foreach loop you are again triggering the findElement method on each and every element until you break from the loop and inside the loop you are triggering the getText method. This is definitely an overkill. Better write a locator that will query you that specific element, something like //span[text() == 'valueOfelementNameVariable'] and then use WebDriverWait class and wait for element to be available for interaction.

For instance you could use the visibilityOfElementLocated expected condition like:

WebElement yourSpanElement = (new WebDriverWait(driver, 60))
  .until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//span[text() == \"valueOfelementNameVariable\"]")));

The until function of WebDriverWait will poll the DOM until the ExpectedCondition returns true or timeout occurs. To be more precise:

public V until(com.google.common.base.Function isTrue)

Repeatedly applies this instance's input value to the given function until one of the following occurs:

the function returns neither null nor false,
the function throws an unignored exception,
the timeout expires,
the current thread is interrupted

If you look at the implementation of visibilityOfElementLocated

  /**
   * An expectation for checking that an element is present on the DOM of a page
   * and visible. Visibility means that the element is not only displayed but
   * also has a height and width that is greater than 0.
   *
   * @param locator used to find the element
   * @return the WebElement once it is located and visible
   */
  public static ExpectedCondition<WebElement> visibilityOfElementLocated(
      final By locator) {
    return new ExpectedCondition<WebElement>() {
      @Override
      public WebElement apply(WebDriver driver) {
        try {
          return elementIfVisible(findElement(locator, driver));
        } catch (StaleElementReferenceException e) {
          return null;
        }
      }

      @Override
      public String toString() {
        return "visibility of element located by " + locator;
      }
    };
  }

It is already ignoring the staleness of element (means it will return null if the element is stale and the polling continues) and the same function will return you the element once it is found or throw an exception if something that is described above happens.


On a larger question, is there a good fix for stale element error?

You should use the Page Object design model. If you initiate all you element using PageFactory, among with a lot of other benefits it brings (mostly in terms of maintenance), you will also lose most of the staleness problems because in this case WebElements are evaluated lazily, that means that lookup of an element is performed every time you call that element.

Sign up to request clarification or add additional context in comments.

5 Comments

This is the best post I've seen on the topic. Thanks for your in-depth explanation. I am very familiar with visibliityOfElementLocated, but I was trying something else. I had been using this to verify the presence/absence of elements after creating/deleting. Instead of looking for the element in the table, I now will probably try and find it like you said (with a better selector) and catch the error. Do you have a better recommendation than that?
I am not sure if I understood your problem, but the invisibilityOfElementLocated expected condition is checking that an element is either invisible or not present on the DOM. Alternatively you can use not expected condition that is the logical opposite condition of the given condition. Reference
Actually I tried your selector from above and it did not work. The HTML looks like this: <span class="primary-click">Mark's Device</span>. My selector looks like this: WebElement device = driver.findElement(By.xpath("//span[text() == \"Mark's Device\"]"));. Do you know why it wouldn't work?
I think you should escape also the single quote: WebElement device = driver.findElement(By.xpath("//span[text() == \"Mark\'s Device\"]"));
Yeah, I figured that out. I forgot to post back. So my final question. I have implemented the xpath lookup, but Safari does not seem to handle it well. It works just find in chrome and Firefox. In Safari, I get a stale element after these lines: String deviceName = "Janes's Macbook Pro"; String newName = "Mark's Macbook Pro"; WebElement device = driver.findElement(By.xpath("//span[.=\"Janes\'s Macbook Pro\"]")); device.click(); The last line opens a modal window. Why would I get a Stale Element now?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.