Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 914
George Shuklin Avatar asked Apr 15 '13 05:04

George Shuklin


People also ask

What is the reason of StaleElementReferenceException?

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.

When an element is located and just after the page is refreshed what kind of exception is expected?

ElementNotVisibleException will be thrown. In this case, the exception is thrown even if the page has not loaded completely. Avoiding-And-Handling: There are two ways to do this. We can either use wait for the element to get completely.

What is the difference between StaleElementReferenceException and NoSuchElementException?

Most common Exceptions: 1) NoSuchElementException : FindBy method can't find the element. 2) StaleElementReferenceException : This tells that element is no longer appearing on the DOM page. 3) TimeoutException: This tells that the execution is failed because the command did not complete in enough time.


1 Answers

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);
like image 181
nilesh Avatar answered Nov 02 '22 14:11

nilesh