Question

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.

Was it helpful?

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
   */

OTHER TIPS

For Question 1) The concept in Page Factory pattern is to identify WebElements only when they are used in any operation.

For Question 2) Whenever you try to access the Page class variables (WebElement or List) @FindBy triggers FindElement or FindElements based on the variable type.

class LoginPage{
.....
@FindBy(id, "uname")
WebElement username;// no trigger

@FindBy(xpath, "//table/tr")
List<WebElement> pdtTable; // no trigger
.....
.....

public void enterUserame(String text){
    uname.sendKeys(text);
}

.....
.....
}

.....
.....
LoginPage loginPage = PageFactory
  .initElements(driver, LoginPage.class); // creates WebElement variables but not triggers


if(loginPage.uname.isDisplayed()){// Trigger happens
loginPage.enterUserame("example");// Trigger happens
}
int count=pdtTable.size();// Trigger happens for FindElements

Additional Info : PageFactory annotation @CacheLookup is used to mark the WebElements once located so that the same instance in the DOM can always be used. This annotation, when applied over a WebElement, instructs Selenium to keep a cache of the WebElement instead of searching for the WebElement every time from the WebPage. This helps us save a lot of time.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top