George Shuklin
George Shuklin

Reputation: 7887

selenium.common.exceptions.StaleElementReferenceException during page refresh

Page contains some data (table with few rows). There is 'refresh' button which reload and redraw some elements on page without reloading static data (ajax).

I'm trying to create proper test for that page, but getting StaleElementReferenceException sometimes.

My code (python):

from selenium import webdriver
browser=webdriver.Firefox()
browser.get('http://mytisite')
browser.implicitly_wait(10)
browser.find_element_by_id('start').click()
while browser.find_element_by_id('status').text!='Done':
    browser.find_element_by_id('refresh').click()
    for row in browser.find_elements_by_class_name('datarow'):
        if not is_correct(row.text):
           print "incorrect"
    time.sleep(10)

1 of 5 iterations fails on line "if not is_correct(row.text)":

selenium.common.exceptions.StaleElementReferenceException: 
 Message: u'Element not found in the cache - perhaps the page 
 has changed since it was looked up' ; Stacktrace: Method 
 fxdriver.cache.getElementAt threw an error in 
 resource://fxdriver/modules/web_element_cache.js 

Main problem: page already contains previous data, so I'm getting race between ajax refresh and webdriver's element query of find_elements_by_class_name('datarow').

How can I proper solve race between ajax refresh and webdriver? Thanks.

Upvotes: 5

Views: 13976

Answers (2)

nilesh
nilesh

Reputation: 14287

I am afraid, I cannot help you with python binding. However I can help you with general advice on WebDriver and some Java snippets that may help you (others) lead to find their python equivalent. There are a bunch of things you can do handle AJAX in Selenium.

1. Implicit waits- This will tell web driver to poll the DOM for a certain period.

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

2. Explicit waits - I prefer these! One can use these in conjunction with implicit waits. You can tell WebDriver to wait for certain condition. You need to use WebDriverWait to use explicit waits.

WebDriverWait wait = new WebDriverWait(driver, 60/*timeout in seconds*/);
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("refresh")));

ExpectedConditions provides many wait conditions for free and I find it extremely useful.

If I were to write your above code snippet in Java, I would do below. Note I wrote this in an editor, so it should compile. I guess this will give you an idea on the use of WebDriverWait.

WebDriverWait wait = new WebDriverWait(driver, 60/*timeout in seconds*/);
ExpectedCondition<Boolean> untilIFindStatus = ExpectedConditions
                    .elementToBeSelected(By.id("status"));
while (wait.until(untilIFindStatus)) {
        WebElement refresh = wait.until(ExpectedConditions
                        .elementToBeClickable(By.id("refresh")));
        refresh.click();
        List<WebElement> allRows = wait.until(ExpectedConditions
                .presenceOfAllElementsLocatedBy(By.className("datarow")));
             for (WebElement row : allRows) {
                if (row.getText().equals("awesome"))
                    System.out.println("do something");
             }
 }

Lastly, in my experience many modern AJAX applications use jQuery. I have used jQuery.active flag over several years successfully to check whether the page is loaded or not. You can combine that with plain javascript document.readyState. This is the general method I use for wait after every 'click' for my AJAX apps.

ExpectedCondition<Boolean> jQueryActive_toBeZero = new ExpectedCondition<Boolean>()   {
        public Boolean apply(WebDriver driver) {
           try {
            return ((Long) jsExecutor
                .executeScript("return jQuery.active") == 0) ? true
                            : false;
         } catch (WebDriverException e) {
             log.debug("It looks like jQuery is not available on the page, skipping the jQuery wait, check stack trace for details");
             return true; //skip the wait
         }
           }
       };
ExpectedCondition<Boolean> document_readyState_toBeComplete = new ExpectedCondition<Boolean>() {
        public Boolean apply(WebDriver driver) {
          return jsExecutor.executeScript("return document.readyState")
             .toString().equals("complete") ? true : false;
         }
     };
    wait.until(jQueryActive_toBeZero);
    wait.until(document_readyState_toBeComplete);

Upvotes: 4

Surya Deepak
Surya Deepak

Reputation: 431

A stale element reference exception is thrown in one of two cases, the first being more common than the second:

  • The element has been deleted entirely.
  • The element is no longer attached to the DOM

please go through the below selenium documentation for more info

Stale element reference exception documentation

Upvotes: 3

Related Questions