Dealing with StaleElementException in Appium tests with PageFactory

How can I handle StaleElementReferenceException in Appium using PageFactory when the execution is too fast? I’m getting the StaleElementReferenceException error with the message ‘The element does not exist in DOM anymore’ while trying to return the attribute from a PageObject method. Additionally, I’m unable to store the element reference for further interaction with the element, and if I do interact with the element, it throws the StaleElementReference Exception.

Partial Solution for The Problem

StepDefinition Class:

// connectivity check success message
    private static final String CONNECTIVITY_CHECK_SUCCESS_MESSAGE = "green_Connectivity check";


// asserts that the connectivity check has passed
   @And("The connectivity check should be passed")
    public void theConnectivityCheckShouldBePassed() {
        softAssert.assertEquals(dayPrepPage.getConnectivitySuccessfulMessage(), CONNECTIVITY_CHECK_SUCCESS_MESSAGE);
    }

PageObject Class:

// content-desc String
private static final String CONTENT_DESCRIPTION_ATTRIBUTE = "content-desc";

// locates the element using PageFactory
    @AndroidFindBy(accessibility = "green_Connectivity check")
    @CacheLookup
    private WebElement _connectivityCheckPass;

//  returns the success message of the connectivity check element
    public String getConnectivitySuccessfulMessage() {
        return waitForElement(_connectivityCheckPass);
    }

// finds and returns the content-desc attribute of the element
    public String waitForElement(WebElement element) {
        WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(60));
        return wait.pollingEvery(Duration.ofNanos(500))
                .ignoring(StaleElementReferenceException.class)
                .until(ExpectedConditions.refreshed(
                        ExpectedConditions.visibilityOf(element)
                )).getAttribute(CONTENT_DESCRIPTION_ATTRIBUTE);
    }

The problem with the above solution is that it is too flaky and highly dependent on the polling time of the WebDriver Wait. A little change to polling or application behaviour will break the test case.

Desired Solution to The Problem

ElementWrapper Class

public class ElementWrapper {
    private final AndroidDriver driver;
    private final By locator;
    private WebElement element;

    public ElementWrapper(AndroidDriver driver, By locator) {
        this.driver = driver;
        this.locator = locator;
    }

    public WebElement get() {
        if (element == null || isStale()) {
            WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(40));
            wait.pollingEvery(Duration.ofNanos(5000));
            element = wait.until(ExpectedConditions.presenceOfElementLocated(locator));
        }
        return element;
    }

    public boolean isStale() {
        try {
            element.isEnabled();
            return false;
        } catch (NoSuchElementException | StaleElementReferenceException e) {
            return true;
        }
    }
}

Waiting Class

public WebElement waitForPresenceOfElement(By element) {
        try {
            ElementWrapper elementWrapper = new ElementWrapper(driver, element);
            WebElement item = elementWrapper.get();
            System.out.println("Found Element: " + item.getText() + "Content Description: " + item.getAttribute("content-desc"));
            return item;
        } catch (Exception e) {
            logger.logError(String.format("TimeoutException: Elements not visible: " + e.getMessage()));
        }
        return null;
    }

I want to be able to store the reference of the element or cache it and use it when required by checking that if the element is stale then try to locate the element again and interact with it.

Failed Approaches to The Problem

I tried to find the element directly by not using the page factory for more control but as the execution is lightning fast even if I do find the element I am unable to perform any action on it.

public WebElement retryWhileLoop(By locator, int timeToRetry) {
        WebElement foundElement = null;
        int repeat = 0;
        while (repeat <= timeToRetry) {
            try {
                foundElement = driver.findElement(locator);
                break;
            } catch (StaleElementReferenceException e) {
                e.printStackTrace();
            }
            repeat++;
        }
        if (foundElement == null) {
            throw new NotFoundException("The element was not located: " + connectivityCheckPass.toString());
        }
        return foundElement;
    }

Don’t hesitate to ask for more information or invite me for a call. I will be more than happy to collaborate to figure this out. Thank you.

Disclaimer: I do not know any details other than the ones you provided.
If my suggestions are invalid to you, please skip them.

I will poke here around and would suggest to ask yourself:

  • Why is it important to interact with an element with which even not the user can interact with?
  • If its really important for whatever reason:
    • Are there maybe other sources where you can get the information from? e.g. from API calls (Either send requests or received responses)
    • When you step back a bit, what in general others ways can you think of to achieve what your are trying her? What is the situations of a user you want to cover and can you maybe find another ways?

Hey @sebastian_solidwork , Thank you for sharing your insights on this.

There is an activity in the application by the name of Day Preparation it checks for the health of the overall application if everything is running fine or not and there are currently 10 items to check for.

There are currently Four states

  1. Gray - The check has yet to start
  2. Blue - The check is in progress
  3. Green - The check is completed successfully
  4. Red - The check has failed

These states are displayed in the drawable image which changes pretty quickly and there are some dialogues as well which open on the completion of some checks in between which makes the synchronization even more problematic as I lose reference to the object pretty quickly failing to get the desired attribute.

Just that I get that right: you want to know something from this fast changing processes?
What would you lose if you do not get it? Sometimes UI automating isn’t the proper tool.

Is there any chance that you slow the process down?
E.g. can you control the data which is process and make it big enough? Or slowing down the network speed?

I have a broad experience with UI automation, but my latest experiences with Appium and WebDriver lay a few years in the past.

@sebastian_solidwork , I understand your point of view on this topic thanks for valuable input. For now I am going to work with what I have and then move to API and Unit level testing for this particular scenario which indeed might be more useful and robust strategy.

1 Like

@azeembaloch You are getting these exceptions because Appium deals with the screen conceptually incorrectly. Find out more details here: https://testrigor.com/staleelementreferenceexception/ There is another way to avoid having those exceptions, checkout testrigor.com - it will handle those situations for you automatically. Here is how it works: https://youtu.be/ivVHNrEu7y8 Disclaimer: I’m a co-founder :slight_smile:

Thank you @testrigor. I will try it out, next time. :heart: