Search code examples
seleniumselenium-webdriverwebdriverpageobjects

when do FindBy attributes trigger a driver.FindElement?


My question is: do webelements decorated with findby attributes call the findelement function upon each reference to them? If not, when?

And what is the procedure with List< webelement > which is also decorated? Does it trigger when you reference the list, or when you reference an element inside that list?

I'm asking because I have some situations where I'm getting stale element exceptions and I want to know how to deal with them.


Solution

  • WebElements are evaluated lazily. That is, if you never use a WebElement field in a PageObject, there will never be a call to "findElement" for it. Reference.

    If don't want WebDriver to query the element each time, you have to use the @CacheLookup annotation.

    What about the list part of my question?

    The findElements is triggered when you query from the list. Say you have:

    @FindBy(xpath = "//div[@class=\"langlist langlist-large\"]//a")
    private List<WebElement> list;
    

    Following code samples all trigger the findElements :

    list.isEmpty();
    

    WebElement element = list.get(0);

    Where as

    List<WebElement> newList = new ArrayList<WebElement>();
    newList = list;
    

    does not trigger the findElements().

    Please check the LocatingElementListHandler class. I suggest diving into the source for answers.


    You may find this code comment from PageFactory class helpful:

    /**
       * Instantiate an instance of the given class, and set a lazy proxy for each of the WebElement
       * and List<WebElement> fields that have been declared, assuming that the field name is also
       * the HTML element's "id" or "name". This means that for the class:
       * 
       * <code>
       * public class Page {
       *     private WebElement submit;
       * }
       * </code>
       * 
       * there will be an element that can be located using the xpath expression "//*[@id='submit']" or
       * "//*[@name='submit']"
       * 
       * By default, the element or the list is looked up each and every time a method is called upon it.
       * To change this behaviour, simply annotate the field with the {@link CacheLookup}.
       * To change how the element is located, use the {@link FindBy} annotation.
       * 
       * This method will attempt to instantiate the class given to it, preferably using a constructor
       * which takes a WebDriver instance as its only argument or falling back on a no-arg constructor.
       * An exception will be thrown if the class cannot be instantiated.
       * 
       * @see FindBy
       * @see CacheLookup
       * @param driver The driver that will be used to look up the elements
       * @param pageClassToProxy A class which will be initialised.
       * @return An instantiated instance of the class with WebElement and List<WebElement> fields proxied
       */