Handling StaleElementReferenceException

hi, my first time posting so hope to get some help :grinning:
first, I will explain my situation - I am testing an app, both in android and ios. android code works consistently but the ios is flickery due to the Stale exception.
basically, I get to the end page on the app which consists of a lot of data and popups. when I use a popup and click ā€˜Doneā€™, the page is refreshing with the new data according to what I gave it in the popup.
my goal is to catch the element after I used the popup as well and the page was refreshed.
what happens is, that when I click on ā€˜Doneā€™ in the popup, the driver tries to find an element in the page(same page) and a situation which calls ā€˜race conditionā€™ occurs. there is a race between the refresh of the page and appium trying to find the element. because of that the test some times passes and some times donā€™tā€¦

I found some solutions on the web which I want to discuss first in order to understand them better and make a better decision.

  1. to use ExpectedCondition.refreshed - how to use it? should I find the element before the refresh and give it to the webDriverWait after the refresh started? or just use refreshed after the refresh started(without catching the element before the refresh)?
    if the first one is correct - than, should I catch all the element I want to use after the refresh before it happens as well? it might be a lot of elements.
    if the second one is correct - what happens if the refreshed happened before getting to the webDriverWait?

  2. use try catch - find an element, than try to click(example) on it in a try block, if it gets to the catch block, then, try to catch the element again. question - if the Stale exception is thrown does it say that the element already refreshed or I might try to catch it again and the Stale exception will occur again?

  3. overrides ExpectedCondition apply method - may use the try catch option here. any other suggestions?

  4. my employer suggests that the app developer will generate an id to a predetermined element when the page is refreshed (but then it should be deleted for the next time I will want to know if a refresh occurred).

as well, how can we detect that a page is fully reload using appium?

which one do you think I should use, why and how?
thanks for answering and I will love to get some other suggestion to handle it correctly, or some explanation on how to avoid that.
:pray:

1 Like

first add your code. how you look for elements. how tap and so on.
also try to add for iOS:

capabilities.setCapability("waitForIdleTimeout", 0);

log.info(ā€œchange back to default standardā€);
wait.until(visibilityOf(driver.findElements(MobileBy.name(ā€œeditā€)).get(1))).click(); //tigers popup
wait.until(visibilityOfElementLocated(MobileBy.name(ā€œSelect Standardā€))); //waiting for the popup title
wait.until(elementToBeClickable(MobileBy.name(ā€œstandardā€))).click();
wait.until(elementToBeClickable(MobileBy.name(ā€œDoneā€))).click();
//popup closed
//refreshing
//looking for elements
for example:
log.info(ā€œcheck number of activitiesā€);
String activities = wait.until(presenceOfElementLocated(MobileBy.name(ā€œActivity (12)ā€))).getText(); //some times fail, and some time passes
assertEquals(ā€œIncorrect number of activitiesā€, ā€œActivity (12)ā€, activities);

*it is a bit hard coded now, there are things that need to be changed.
@Aleksei

  1. try switch to ā€˜tapā€™ instead of ā€˜clickā€™. I like better -> http://appium.io/docs/en/commands/interactions/actions/.
    also you can use http://appium.io/docs/en/commands/interactions/touch/tap/ or https://appium.io/docs/en/writing-running-appium/touch-actions/

lets say I am moving to the next page and trying to find an element with the same selector as in the previous page. I would try to find an unique element (of the second page) first and then try to search the desirable element.
if I find the unique element does it insure me that all the other elements are rendered as well?
maybe just its children? (I guess that each element renders to the page on a different time).

or maybe there is a way to detect that a page is fully loaded, in web context you can use document.reayState == ā€œcompleteā€. is there any way to do that with mobile commands?

it is basically the same problem as refreshed page
@Aleksei

so rewrite your code in following way:

  1. before we do anything we identify what page we have now. something like:
        myApp()
                .homePage()
                .tapAddMoneyButton() // we have tap and expecting new page -> 
                .isAddMoneyPageLoaded() // check we really have expected page
                .tapInstantTopUpCard()
  1. in your case more immediate effect is make ONE click function and add minor sleep right after tap to allow page start changing. so code should look like:
        log.info(ā€œchange back to default standardā€);
        click(wait.until(visibilityOf(driver.findElements(MobileBy.name(ā€œeditā€)).get(1)))) //tigers popup
        wait.until(visibilityOfElementLocated(MobileBy.name(ā€œSelect Standardā€))); //waiting for the popup title
        click(wait.until(elementToBeClickable(MobileBy.name(ā€œstandardā€)));
        click(wait.until(elementToBeClickable(MobileBy.name(ā€œDoneā€)));

    protected void click(MobileElement el) {
        el.click();
        sleep(100); // 100 ms
    }

yes, I get you, but how do I write the isAddMoneyPageLoaded()? or what check should be in it.
because if I check for an unique element it does not mean the other elements(including the one I want to click) are rendered already. maybe just the unique element is rendered.
@Aleksei

an example for my error
ERROR - The previously found element ā€œā€œDoneā€ Buttonā€ is not present in the current view anymore. Make sure the application UI has the expected state. You could also try to switch the binding strategy using the ā€˜boundElementsByIndexā€™ setting for the element lookup. Original error: No matches found for Identity Binding from input {(
Button, {{272.5, 20.0}, {43.5, 44.0}}, label: ā€˜Doneā€™
)}

To make isSomePageLoaded you need find some unique element. No such unique ID -> ask dev to add it. I do it myself for iOS code. For Android this is not a problem for our app.

PS but temporarily add second solution. It will reduce much ā€˜StaleElementā€™ error. Later with checking page loaded it will 100% vanish.

Try to switch the binding strategy like it is recommended in the error message

hi, just want to thank you for helping. eventually the problem occurred because the page rendered each second for updating the content(data) on the page.

1 Like

Hi, Iā€™m also worrying about the above stale element reference issue. What is the solution that you found and can you share it?

If button Done doesnā€™t change the position after refreshing the page. Before the page is loaded, find the element to get the coordinate width and height. After the page is loaded, tap on coordinate at point (x+width/2, y+height/2)

Hey guys,
I created this topic in the Appleā€™s forum, maybe if everyone send some message to bump up this subject, we can get some help from the Apple on it:

https://developer.apple.com/forums/thread/749918