3

While using Pagefactory I am directly declaring WebElement as below.

@AndroidFindBy(accessibility = "androidLocator")
@iOSFindBy(accessibility = "iosLocator")
private MobileElement element;

But, is there a way out to handle StaleElementReference exception as I am not using any By object here. All the solutions that I could figure out asks me to use locators as object of By.

I wanted to write a generic method in a parent class for all the page classes which handles StaleElementReferenceException. But the problem is I can only pass the reference as a WebElement and not as a By object which beats the purpose of reinitialize the WebElement.

I could find the below solution :

FluentWait<MobileDriver<MobileElement>> wait = new FluentWait<MobileDriver<MobileElement>>(driver)
                        .withTimeout(20, TimeUnit.SECONDS).pollingEvery(500, TimeUnit.MILLISECONDS)
                        .ignoring(NoSuchElementException.class).ignoring(StaleElementReferenceException.class);
                wait.until(new Function<WebDriver, MobileElement>() {
                    @Override
                    public MobileElement apply(WebDriver driver) {
                        element.get
                        MobileElement element = driver.findElement(by);
                        return element;
                    }
                });

But the same problem occurs here too. I need to pass the reference as By object where as in PageFactory I have reference as WebElemrnt

Guy
  • 46,488
  • 10
  • 44
  • 88

4 Answers4

1

You could use refreshed ExpectedCondition to wait for the element to be redrawn in the DOM

(new WebDriverWait(driver, 30)).until(ExpectedConditions.refreshed(ExpectedConditions.visibilityOf(element)));
Guy
  • 46,488
  • 10
  • 44
  • 88
  • Not working even after implementing this. The new exception introduced is : `Expected condition failed: waiting for condition (visibility of [[Android: uiautomator2] -> accessibility id: locator]) to be refreshed (tried for 20 second(s) with 500 MILLISECONDS interval` – Amitakhya Bhuyan Oct 22 '18 at 08:41
1

Whether it be Appium or plain old Selenium, my solution for stale element has always been to make sure I'm working with a freshly instantiated page object.

If you are sharing a page object across test methods, or if there is something that might change the state of the page, it can't hurt to re-initialize the page object.

You don't show your page object initialization code, though. What does your page initialization look like?

Bill Hileman
  • 2,798
  • 2
  • 17
  • 24
0

You can put your command in a try...catch block and if you catch StaleElementReference Exception then refresh your page using driver.navigate.refresh() and perform the same action again.

If your element is going to be refreshed automatically after some time then you can also use ExpectedConditions for this scenario.

wait.until(ExpectedConditions.refreshed(ExpectedConditions.stalenessOf(element)));

  • As OP said he already has WebElement defined in POM, so driver.findElement will just cause you to use another locator object. And you can do click and sendKeys if the element is not stale, but if it is in stale state it will throw the StaleElementReference Exception, that's why I suggested to refresh the page in catch block so that the element gets reloaded into the DOM and then user can perform any action on it. – Hiren Barad Oct 21 '18 at 05:22
  • Refreshing the page is not a good option, as refreshing will delete all the previously filled data. Hence, the test case will fail. – Amitakhya Bhuyan Oct 22 '18 at 06:01
  • Then you can handle the data filling part in an external method and put that method call in a try..catch block, if it throws StaleElementReference Exception for any of the element it will refresh the page and try to rewrite data in all the elements. If it's a multi step process and refreshing the page navigates you to the first page then this might not be a good idea but if you are staying on the same page, i think this should work. Other than that I don't see any other option then waiting for the element to get refreshed automatically, if there is any logic implemented to do that. – Hiren Barad Oct 22 '18 at 06:21
  • Ya got it. This might be helpful for browser automation. But if you look at my code snippet above, I am more focussed about native app automation using appium. – Amitakhya Bhuyan Oct 22 '18 at 07:34
  • Can you try PageFactory.initElements(new AppiumFieldDecorator(driver, DEFAULT_WAIT_TIME, SECONDS), this); as suggested over here https://discuss.appium.io/t/staleelementreferenceexception-with-uiautomator2/20138/8. I don't have much experience with appium but I try my best to understand. – Hiren Barad Oct 22 '18 at 08:20
  • Thanks for sharing this link. But it was just adding implicit wait, so no luck on the StaleElement exception. :) – Amitakhya Bhuyan Oct 22 '18 at 08:50
0

You can use a try catch block, in try you can use the normal selenium method to wait and click. And then in catch you can use the JavascriptExecutor to click on the element.

private WebDriverWait webDriverWait;

public WebElement waitForElement(WebDriver driver, WebElement element) {
    try {
        webDriverWait = new WebDriverWait(driver, 10);
        webDriverWait.until(ExpectedConditions.elementToBeClickable(element));
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return element;
}

public void click(WebDriver driver, WebElement element) {
    try {
        waitForElement(driver, element).click();
    } catch (Exception ex) {
        ex.printStackTrace();
        JavascriptExecutor js = (JavascriptExecutor) driver;
        js.executeScript("arguments[0].click();", element);
    }
}

I hope this solves your problem. Thanks.

Suraj Jogdand
  • 308
  • 2
  • 17
  • As u can see in the question, it is appium code. So, javascript executor don't be handy here. More over I am not just concerned about click, it can be getText or and other method in the WebElement Class. – Amitakhya Bhuyan Oct 22 '18 at 05:48