Handle StaleElementReferenceException while using PageFactory

Viewed 3748

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

4 Answers

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)));

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?

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)));

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.

Related