How PageFactory Could Help to Handle StaleElementReferenceException

Already , I have covered cause and ways of handling StaleElementReferenceException in detail. You can find the link of that post below:-

StaleElementReferenceException – Element’s “Reference” Stales – Get New Reference Of Element

Above post gives you a detailed information about :-

  1. What is StaleElementReferenceException?
  2. What are causes of StaleElementReferenceException ?
  3. What are the ways of handling StaleElementReferenceException ?
  4. Did use of Sleep for a thread help in handling StaleElementReferenceException?
  5. Did PageFactory help in handling StaleElementReferenceException ?
  6. How Retry mechanism to relocate WebElement may help in best way?

In this post, I am going to explain in detail – How PageFactory could resolve StaleElementException to an extent and where it may fail.

Note here that I am using term “PageFactory“. Many people say using Page Object Model (POM) we can hanle StaleElementReferenceException which is not correct.

  • PageFactory is a default mechanism provided by Selenium WebDriver to implement Page Object Model using FindBy , FindBys and FindAll annotations. It helps to remove some boiler-plate code from your Page Objects.
  • As per official document, FindBy and related annotations are used to mark a field on a Page Object to indicate an alternative mechanism for locating the element or a list of elements. We can either use this annotation by specifying both “how” and “using” or by specifying one of the location strategies (eg: “id”) with an appropriate value to use. For example :-
@FindBy(id= "email_create")
protected WebElement emailAddress;
  • PageFactory instantiates an instance of the given class, and set a lazy proxy for each of the WebElement andList fields that have been declared.
  • PageFactory will search for an element on the page that matches the field name of the WebElement in the class. It does this by first looking for an element with a matching ID attribute. If this fails, the PageFactory falls back to searching for an element by the value of its “name” attribute.
  • To change how the element is located, use the above annotations with locating strategy.
  • By default, the element or the list is looked up each and every time a method is called upon it. For example:- If we have a button web element and we call “click” N times on that button, WebDriver will look for element freshly N times. You no need to worry to initialize web elements once you use initElements() method of PageFactory. You will not face NullPointerException as well as StaleElementReferenceException. PageFactory helps a lot in automating AJAX-heavy applications because of this feature.
  • We can change default behavior of looking for element every time using @CacheLookup. We can use this if page is static.

Now you should get clear concept that how PageFactory helps to avoid StaleElementException to an extent. Plain Page Object Model(POM) can not help you. In that case you need to go for retry mechanism.

Let’s do an example in which we will see how PageFatory resolves StaleElementReferenceException.

  1. Load URL “https://github.com/login”.
  2. Type wrong username.
  3. Type wrong password.
  4. Click on submit.
  5. Type correct username.
  6. Type correct password.
  7. Click on submit.

Selenium-Java Code :-

package StaleElementException;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.Test;

import io.github.bonigarcia.wdm.WebDriverManager;

public class StaleElementHandling {

        @Test
        public  void main() throws InterruptedException {
                // Setup browser
                WebDriverManager.chromedriver().setup();
                WebDriver driver = new ChromeDriver();
                // Open URL
                driver.get("https://github.com/login");
                // locate and type user name
                WebElement username = driver.findElement(By.id("login_field"));
                username.sendKeys("amod");
                // locate and type password
                WebElement pass = driver.findElement(By.id("password"));
                pass.sendKeys("amod");
                // locate and click on submit
                WebElement sub = driver.findElement(By.xpath("//input[@value='Sign in']"));
                sub.click();
                Thread.sleep(10000);
                // again type user name
                username.sendKeys("amod");
        }
}

Output :-

Starting ChromeDriver 81.0.4044.69 (6813546031a4bc83f717a2ef7cd4ac6ec1199132-refs/branch-heads/4044@{#776}) on port 5084
Only local connections are allowed.
Please protect ports used by ChromeDriver and related test frameworks to prevent access by malicious code.
May 01, 2020 4:36:11 AM org.openqa.selenium.remote.ProtocolHandshake createSession
INFO: Detected dialect: W3C
FAILED: main
org.openqa.selenium.StaleElementReferenceException: stale element reference: element is not attached to the page document
  (Session info: chrome=81.0.4044.122)
For documentation on this error, please visit: https://selenium.dev/exceptions/#stale_element_reference

So ,we get StaleElementReferenceException at line username.sendKeys(“amod”); because we tried to use a reference which belonged to old DOM. DOM changed when we passed wrong username and password. I have explained it very well in StaleElementReferenceException – Element’s “Reference” Stales – Get New Reference Of Element .

Now we know the reason of occurring StaleElementReferenceException and also unique feature of PageFactory to look for web element every time whenever a method is called on it. Are you able to connect dots? This problem can be solved using PageFactory. Let’s try it.

Create a page class and initialize its WebElement/s :-

package StaleElementException;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;

public class GitHubLoginPage {
        WebDriver driver;
        @FindBy(id = "login_field")
        public WebElement username;
        @FindBy(id = "password")
        public WebElement password;
        @FindBy(xpath = "//input[@value='Sign in']")
        public WebElement submit;

        public GitHubLoginPage(WebDriver driver) {
                this.driver = driver;
                PageFactory.initElements(driver, this);
        }
}

Use above Page Object class in test as below :-

package StaleElementException;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

import io.github.bonigarcia.wdm.WebDriverManager;

public class StaleElementHandling2 {
        public static void main(String[] args) throws InterruptedException {
                // Setup browser
                WebDriverManager.chromedriver().setup();
                WebDriver driver = new ChromeDriver();
                driver.get("https://github.com/login");
                // Creating object of page class which will initialize web elements
                GitHubLoginPage lp = new GitHubLoginPage(driver);

                lp.username.sendKeys("amod");
                lp.password.sendKeys("dsds");
                lp.submit.click();
                Thread.sleep(5000);
                // Same element
                lp.username.sendKeys("amod");
                lp.password.sendKeys("dsds");
                lp.submit.click();
        }
}

You will not face StaleElementException becuase PageFactory will look for WebElement again after you provided wrong credentials and DOM was changed. But you may face this exceptions many times in angular and react applications where Web page and its components refreshes frequently.

More you understand an application , More stable script you can write.

If you have any doubt, feel free to comment below.If you like my posts, please like, comment, share and subscribe.#ThanksForReading

#HappyLearning