How WebDriverWait works differently for WebElements defined by driver.findElement versus PageFactory initialization

When it comes to using WebDriverWait to wait for WebElement properties,like text or attributes, when sending WebElements as properties generated via PageFactory, that will work properly. However when using ‘driver.findElement()’ to identify the WebElements required as parameters in the WebDriverWaits, that will fail. Here is the reason why.

The ‘findElement()’ method

If you went through the official documentation, you might have noticed an explanation related to the ‘findElement()’ method behavior. If you use it in a test (something like ‘driver.findElement(someSelector)’), this expression gets evaluated on the spot. This means: if the WebElement you are trying to identify by calling this method is present at the time this line of code executes, all is well. If the WebElement is not at all present in the page’s DOM, this line of code will throw a NoSuchElementException. So, as example, if you open a page, then want to click on a button that you identify by using ‘driver.findElement()’, the code would look something like this:

driver.get(‘theDesiredUrl’);
driver.findElement(By.cssSelector(‘someCSSSelector’)).click();

If at the time the ‘findElement’ code executes, the WebElement identified by the ‘someCSSSellector’ selector is not present in the DOM, the test containing this step will fail at this line of code. Therefore, when you want to execute this line of code, and you want to do it successfully, the ‘selector’ must be present in the page’s DOM. Or saying differently, if you inspect the HTML of the page, you have to find that selector. Let’s say the selector is “.myClass h2 p”. If no such ‘text’ is found in the HTML when you run the test, it will fail.

So, remember one key thing for now: this expression gets evaluated on the spot. Also, you can think about this situation this way: when the ‘selector’ gets ‘evaluated’ by any code, it needs to be in the HTML already, for it not to cause an error. You’ll see what I mean when I will refer to the page object class behavior.

Using PageFactory

Let’s say you want to create a Page Object class in which you want to define all the WebElements you will use in a test. If we were to write the same WebElement we did before, but by placing it inside a Page Object class while using annotations, it will look like:

@FindBy(how = How.CSS, using = “someCSSSelector”) public WebElement theElement;

In the test that will use this WebElement, before actually using it, you will need to initialize the class that holds it via PageFactory, as follows, after creating the driver instance (let’s say for this example you are storing the WebElements in the ‘ExamplePage’ class):

ExamplePage page = PageFactory.initiElements(driver, ExamplePage.class);

This will initialize the page, but the WebElements defined in the page class don’t need to be present at the time this happens. This class can be seen as a gathering of WebElements that are somehow related and make sense to be declared together. They might be on the same page, or in the same module on a page. The good part when using this approach is that you can write all the selectors in the PageObject class, and have them there, available when you will actually need to interact with them.

The WebElements declared in a PageObject class as above are lazily loaded. This means the check that the selector corresponding to a WebElement is present in the page is only done when you explicitly interact with it, like when you want to click on it. The selector does not need to be present in the HTML when you initialize the PageObject class through PageFactory. Also, the selector does not need to be present in the HTML when you pass the variable as a parameter to the method. Only when you are interacting with it!

Confusing enough. Maybe it will be clearer when I go over how these two different approaches work with the WebDriverWait methods.

The wait method

Let’s take the easiest example: waiting for a WebElement to be displayed. For this we will create a wait method, called ‘waitForElementDisplayed’. It will only take one parameter: a WebElement. The signature for this method looks like:

public void waitForElementDisplayed(WebElement element) {…};

The code inside the method is not relevant for this post, and you can actually find it in ‘thewaiter’ library if you need it.

Let’s see how it is used and what happens for driver.findElement versus PageFactory.

For findElement()

The usage in this case would look like:

waitForElementDisplayed(driver.findElement(By.cssSelector(someSelector));

If the WebElement identified by the selector is present in the DOM, at the time this method runs, there will not be an error. However, you actually want to wait until the element is displayed. That is the purpose of the wait method. So, for the situation when the WebElement is not displayed at the time this method is run, an exception will be thrown. A NoSuchElementException to be more precise.

And this happens exactly because the expression (driver.findElement….) is evaluated on the spot. And since the WebElement is not yet present, it throws an exception right away, and no code from the inside of the wait method will get executed. There will be no waiting for the WebElement to be displayed. So never use this approach. Never send a ‘driver.findElement()’ expression as parameter to a WebDriverWait method.

For use with PageFactory
  • The usage in this case would look like:
waitForElementDisplayed(page.theElement);

Here, even if the WebElement is not present in the HTML at the time this method executes, the code inside the wait method will execute. That is the idea, that is what you want from a wait method. In this case, passing the WebElement as a parameter does not mean the parameter gets evaluated when you call the method, but instead inside the method.

At the method call, there is only a check that a variable with those ‘credentials’ exists (that in the page you mentioned, a ‘theElement’ variable of type WebElement is declared, but not what its’ value is). Once inside the method, the appropriate code will wait for the WebElement to be present in the page’s HTML.

Conclusion

When using a method that holds a WebDriverWait method call to wait for properties of a WebElement, passing the WebElement as a a property that was initialised through PageFactory and created by annotations will work properly. Never use the ‘driver.findElement()’ way of passing the WebElement to the wait method with WebDriverWait, as that will not work.

Checkout my thewaiter library for methods on waiting for WebElement attributes and text.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.