Selenium: How to tell if RemoteWebDriver.findEleme

2020-01-24 10:31发布

问题:

In my understanding such exception can be thrown only if the code operates on a WebElement instance, calling methods on it after the corresponding DOM element has been reloaded or removed.

So in order to find out if RemoteWebDriver.findElements(By) can throw that exception or not I looked for that kind of code in the source of the RemoteWebDriver implementation of WebDriver interface (in selenium-remote-driver-3.8.1.jar). But following the call stacks deep down into RemoteWebDriver's code eventually became too difficult for me.

So my question is: besides trying to figure that out by inspecting Selenium source, are there better ways to tell if a WebDriver method in general - or just findElements(By) in particular - can throw StaleElementReferenceException ? It's a RuntimeException, and the Selenium JavaDoc doesn't have @throws for it.

回答1:

You pinpointed issue about "StaleElementReferenceException", this is related when webElement instances changes, when DOM is changed due some activity on page.

It is related to how WebDriver internally handles references to webElements, and this issue happens when during runtime references to object(s) are changed.

Now, let’s assume when you have fetched the element and before doing any action on it, something got refreshed on your page. It could be the whole page that is being refreshed or some call which has refreshed only a section of the DOM where your element falls.

In this scenario the internal id which WebDriver was using, and stored somewhere in cache has become stale (not referenced anymore), so now for every operation on this WebElement, we will get StaleElementReferenceException.

So to try to avoid it on critical places try to use this method, for determine if element is stale.

public static boolean isStale(WebElement e){
    try{
        e.isDisplayed();
        return false;
    }catch(StaleElementReferenceException ex){
        return true;
    }
}

Usually refreshing of page, pageObject, or just this particular element would help in 90% of cases.

And after refreshing page/element WebDriver will assign a different internal Id to this element, and will be able to use it again without any problem.

This problem is solver with PageObject / PageFactory design patter.

public class SomePage {
    @FindBy(how = How.NAME, using = "q")
    private WebElement searchBox;


    public SomePage() {
        PageFactory.initElements(driver, this);
    }


    public void searchFor(String text) {
        searchBox.sendKeys(text);
    }
}

Hope this helps,



回答2:

StaleElementReferenceException

StaleElementReferenceException extends WebDriverException and indicates that a reference to an element is now "stale" and the element reference is no longer present on the DOM of the page.


Answering your queries one by one:

  • StaleElementReferenceException can be thrown only if the code operates on a WebElement instance, calling methods on it after the corresponding DOM element has been reloaded or removed: You are almost there. Here are the complete complete list of the causes:
    • The element has been deleted entirely.
    • The element is no longer attached to the DOM.
    • The webpage on which the element was part of has been refreshed.
    • The (previous) element has been deleted by a JavaScript or AjaxCall and is replaced by a (new) element with the same ID or other attributes.
  • Solution : If a old element has been replaced with new identical one, the simple strategy would be to use findElement or findElements to look out for the element again.
  • I looked for that kind of code in the source of the RemoteWebDriver implementation of WebDriver interface : From user level perspective it wouldn't add any value even knowing the algorithm or the flow chart. As a end user you need to get a clear understanding of the implementation which will help you to deliver optimized Test Code.
  • Besides trying to figure that out by inspecting Selenium source, are there better ways to tell if a WebDriver method in general - or just findElements(By) in particular - can throw StaleElementReferenceException : There is no single or full proof way to predict whether findElement(By) or findElements(By) can through StaleElementReferenceException or not. If the AUT (Application Under Test) is based on either JavaScript, AjaxCalls or jQuery, most of the WebElements will be dynamic and that's where WebDriverWait comes to our rescue before looking out for the element(s).

References

Here are the references of this discussion:

  • Stale Element Reference Exception
  • Class StaleElementReferenceException
  • StaleElementReference Exception in PageFactory