Stale Element Reference Exception In Selenium

Stale Element Reference Exception In Selenium

 

The Stale Element Reference Exception is one of the infamous exceptions in selenium. This exception is caused when:

  • the element is deleted completely from the DOM.
  • element is no longer associated to DOM.

Every HTML webpage contains elements (buttons, links, checkbox etc.) which are defined in the DOM structure. Every time a web page is loaded/ reloaded, the DOM is constructed/destructed accordingly. Due to the DOM restructure during the execution, Stale Element Refernce Exception is thrown.

Before moving to the solution for this problem, let’s see an example how this execption occurs.

 

Stale Element Reference Exception Example

 

In this example, we will try to perform some operation on a web element. Once the action is performed, we will refresh the browser (through the code) and try to perform same operation on the same web element.

Navigate to https://the-internet.herokuapp.com/entry_ad. A pop up is displayed with some text and Close button. Click on the Close button and then hit refresh to again see the pop up. This refresh operation triggers DOM restructuring and our test will fail if it tries to click on Close button again.

This failure happens due to Stale Element Reference Exception as the reference of Close button is obsolete after the page refresh.

 

Stale Element Reference Exception pop up

 

Below code tries to emulate the Stale Element Reference Exception:

 

package staleElement;

import java.util.concurrent.TimeUnit;

import org.openqa.selenium.By;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

public class StaleElementExceptionDemo {
    
    //Declare and define WebDriver object variable. It is static so that same reference could be used from different classes/methods(which must be static)  
    static WebDriver driver = null;
    public static void main(String[] args) {

        //Close Button locator 
        String close = "//div[@id='modal']//div[@class='modal']//div[@class='modal-footer']//p[contains(text(),'Close')]";
        
        //Point to the path where chromedriver is saved
        String filePath = System.getProperty("user.dir");
        System.setProperty("webdriver.chrome.driver", filePath + "/driver/chromedriver");
        
        //Instantiate the driver object variable and navigate to the URL
        driver = new ChromeDriver();
        driver.get("https://the-internet.herokuapp.com/entry_ad");

        //Wait till pop up window is displayed
        WebDriverWait wait = new WebDriverWait(driver, 30L);
        wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath(close)));
        wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath(close)));

        
        //Store the Close Button xpath reference in some variable of type WebElement 
        WebElement restartModal = driver.findElement(By.xpath(close));
        
        //Click on the Close button. Pop up should get closed
        restartModal.click();

        
        //Refresh/Reload the browser to view the pop up again
        driver.get(driver.getCurrentUrl());
        driver.manage().timeouts().implicitlyWait(10L, TimeUnit.SECONDS);
        
        //Try to click on the Close button pop up
        restartModal.click();
    }
}

 

Below is break down of the code:

  • driver object variable is declared static so that other static methods can access the same driver. The initial value of driver variable is set to null.
  • close variable holds a String value which will be used to create the xpath for Close web element in the pop up.
  • instantiate the driver object variable and navigate to the URL.
  • use Explicit Wait on Close button until it is loaded into DOM and is visible.
  • restartModal variable holds the locator reference for Close button.
  • use click() method on above web element variable, the pop up should get closed.
  • reload the page using driver.get(driver.getCurrentUrl()); At this time, the current DOM structure is destroyed and recreated. Now the new DOM does not have any reference for restartModal web element.
  • wait till browser is loaded using implicit wait.
  • again try to use click() method on restartModal web element, this should throw Stale Element Reference Exception.

 

Solution for Stale Element Reference Exception

 

To start with the solution, consider following points:

  • avoid using the web element variable (for eg restartModal) after the step which require browser refresh. 
  • take the reference of the web element using By keyword and perform all the operations on this variable after page refresh or wherever you encounter Stale Element Reference Exception.

We will write a new static method retryClickElement() which accepts 2 arguments:

  • By locater variable
  • WebDriver instance variable

This method is declared static, hence WebDriver variable (driver) reference is common to this method too.

Below is the complete updated code:

 

package staleElement;

import java.util.concurrent.TimeUnit;

import org.openqa.selenium.By;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

public class StaleElementExceptionDemo {
    
    //Declare and define WebDriver object variable. It is static so that same reference could be used from different classes/methods(which must be static)  
    static WebDriver driver = null;
    public static void main(String[] args) {

        //Close Button locator 
        String close = "//div[@id='modal']//div[@class='modal']//div[@class='modal-footer']//p[contains(text(),'Close')]";
        
        //Point to the path where chromedriver is saved
        String filePath = System.getProperty("user.dir");
        System.setProperty("webdriver.chrome.driver", filePath + "/driver/chromedriver");
        
        //Instantiate the driver object variable and navigate to the URL
        driver = new ChromeDriver();
        driver.get("https://the-internet.herokuapp.com/entry_ad");

        //Wait till pop up window is displayed
        WebDriverWait wait = new WebDriverWait(driver, 30L);
        wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath(close)));
        wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath(close)));

        
        //Store the Close Button xpath reference in some variable of type WebElement 
        WebElement restartModal = driver.findElement(By.xpath(close));
        
        //Click on the Close button. Pop up should get closed
        restartModal.click();

        
        //Refresh/Reload the browser to view the pop up again
        driver.get(driver.getCurrentUrl());
        driver.manage().timeouts().implicitlyWait(10L, TimeUnit.SECONDS);
        
        //Try to click on the Close button pop up
        // restartModal.click();
    
        //###################### Overcome Stale Element exception######################
        
        
        retryClickElement(By.xpath(close), driver);    

    }
    
    
    //method to overcome stale element reference exception
    public static void retryClickElement(By locator, WebDriver driver) {
      
        //variable to count the attempts
        int attempts = 0;
        
        //number of times this code should get executed 
        while(attempts < 2) {
            
            try {
                
                //wait explicitly till desired element is visible and clickable
                WebDriverWait wait = new WebDriverWait(driver, 30L);
                wait.until(ExpectedConditions.visibilityOfElementLocated(locator));
                wait.until(ExpectedConditions.elementToBeClickable(locator));
                
                //click on the locator once it is found and terminate the loop
                driver.findElement(locator).click();
                break;
            } catch(StaleElementReferenceException e) {
            }
            attempts++;
        }
       
    }

}

 

The second restartModal.click(); operation is commented and replaced by the retryClickElement(By.xpath(close), driver); method.

Using the above logic, our program will always send the xpath value instead of web element reference whenever there is a change in DOM. 

 

Author: Dhawal Joshi

A post-graduate in MCA, ISTQB & ITIL certified QA with more than 8 years of experience in QA working with a CMMI Level 5 organization as System Analyst. I started my automation journey with HP UFT(formerly known as QTP) and for the past few years, I am using Selenium for automation. I also have experience in Android Application Development, Java, HTML, and VBScript. When I am not working, I like to spend time with my family, cooking and learning new developments in IT.

Leave a Reply

Your e-mail address will not be published. Required fields are marked *