How to use explicit waits with PageFactory fields and the PageObject pattern

10,893 views
Skip to first unread message

TonyD

unread,
Mar 3, 2012, 1:11:23 PM3/3/12
to webd...@googlegroups.com
Hi,

I know this has been asked before, but it seemed like only work-arounds were posted. I want to bring this up again because I think it's worth considering changing the API to accommodate this.

Basically, I want to use the PageFactory with the PageObject pattern. I think it's great! The only issue is when I have one Page Object transition to another, it sometimes fails when trying to find a WebElement that was located by the PageFactory because the web page did not finish loading.

To give a theoretical example:

Page transition: HomePage to UserRegistrationPage

Action: Click "Register User" link on HomePage leads to UserRegistrationPage

Verify UserRegistrationPage has loaded by: Looking for button with ID "submit"

So basically, on the HomePage class I've defined:

@FindBy(id = "registerUserLink")
private WebElement registerLink;

public UserRegistrationPage registerUser()
{
registerLink.click(); return PageFactory.initElements(driver, UserRegistrationPage.class);
}


On the UserRegistrationPage I've defined the following:

@FindBy(id = "submit")
private WebElement submitRegistration;

public void verifyOnPage()
{
if (!submitRegistration.isDisplayed())
{
throw new IllegalStateException("This is not the registration page");
}
}


The issue then is the above sometimes throws the exception, because the page did not quite finish loading yet.

Here is the problem with the two work-arounds:

#1) Change implicit wait time
- This would be a near-perfect solution. By default the implicit wait is 0. I can up that to 10, 60, etc., and then the page starts working.
- However, this has detrimental impact on running the automated testing when you purposely are testing to see that a WebElement does not exist, etc. The test will then sit there until the full implicit wait time expires.

#2) Add an explicit wait in the verifyOnPage() method
- Yes, we can add a WebDriverWait with
ExpectedConditions or a variant of this, it will work.
- However, the wait mechanism takes a By object and not a WebElement object. This means I cannot make any use of the awesome @FindBy WebElement I defined previously. It means I'd have to copy the By string into the explicit wait.
- This ends up with extra boilerplate code and defeating DRY


Unfortunately I don't really have a full solution to the dilemma. But to summarize what I believe to be ideal:
- Be able to make use of the WebElement object the PageFactory created for me so I don't have to "redo it" by using it's By string elsewhere in a WebDriverWait
- Possibly annotate the WebElement further to imply it's going to be used with an explicit wait with the option of specifying the time value
- This would also imply setting a default global explicit wait for the WebDriver instance
- Don't end up having to resort to the WebDriverWait derivatives, because for the most part it's boilerplate in this situation

The only problem with annotating the WebElement further with explicit wait meta-data is it then prevents it from also being used elsewhere with an implicit wait....but then again, the page should have already been loaded so that shouldn't be an issue!

Wait do you think? Any suggestions? Did I miss something that will solve this? I really want to get the most out of this pattern with the least amount of boilerplate! :)

Thanks.

TonyD

unread,
Mar 3, 2012, 1:26:31 PM3/3/12
to webd...@googlegroups.com
Sorry, I'm just posting again because the formatting looks awful. So here it is again:

Luke Inman-Semerau

unread,
Mar 3, 2012, 2:25:34 PM3/3/12
to webd...@googlegroups.com
I don't use page factory but seems like the expected condition you want is ExpectedConditions.visibilityOf(webelement)

Although it does sound like if the annotations don't support adding an explicit wait of some kind they probably should (if the enhancement doesn't already exist in the issue tracker feel free to add one)

-Luke
--
You received this message because you are subscribed to the Google Groups "webdriver" group.
To view this discussion on the web visit https://groups.google.com/d/msg/webdriver/-/QXBSfWpQUzMJ.
To post to this group, send email to webd...@googlegroups.com.
To unsubscribe from this group, send email to webdriver+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/webdriver?hl=en.

Iain Rose

unread,
Mar 4, 2012, 12:19:08 PM3/4/12
to webd...@googlegroups.com
+1 on finding an ExpectedConditons method that you can use to wait for the page to load.

I created a demo project on GitHub to use as an example for a few questions I posted on StackOverflow. You might be able to use this as an example of how to setup your explicit waits when transitioning to a new page object. 


Specifically here


On a related topic, I gave up using the Page Factory for some of the same reasons you have previously listed and have switched to storing all my locators as By objects instead.

This has worked well for me because ...

1. You don't need to call the page factory at all
2. I can use all my locators with ExpectedConditions
3. I can use all my locators when checking for the presence of an element (driver.findElementsBy(By).size() > 0)
4. All my locators can be stored the same way

I don't think the page factory saves that much time anyway

@FindBy(id = "registerUserLink")
private WebElement registerLink;
vs
By registerLink = By.id("registerUserLink");

sheetal singh

unread,
May 9, 2017, 9:17:56 PM5/9/17
to webdriver
Hi,

I am curious to know doubts raised by Tony

Its been asked long back, i also have similar doubts


1. If i am using PF model, do i need to use explicit/fluent wait in my test cases. Dont PF automatically take care of all Web element.

2 In which cases we have to use AjaxElementLocatorFactory initialization.
If any one element in page coming through Ajax then do we need to use this?

3. In case i have to use explicit wait in my test cases, then how to get WebElement from a page.
case 1: All WebElements in Page are private, do i need to add getters for all WebElements present inside a Page.


@Test
public void test1(){
    ExpectedConditions.elementToBeClickable(homePage.getWebElement())
}


case 2: In some explicit wait we need By locator to be passed, how to get By locator in case of PF pattern

@Test
public void test1(){
    ExpectedConditions.visibilityOfElementLocated(RequireABylocator)

anwesana kanungo

unread,
May 19, 2017, 3:05:08 AM5/19/17
to webdriver
Check this this might help .

package pageobject;


import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.support.CacheLookup;

import org.openqa.selenium.support.FindBy;

import org.openqa.selenium.support.PageFactory;

import org.openqa.selenium.support.ui.ExpectedConditions;

import org.openqa.selenium.support.ui.WebDriverWait;


public class LoginPage extends Driver{


public LoginPage(WebDriver driver) {

super(driver);

PageFactory.initElements(driver, this);

}

@CacheLookup

@FindBy(name = "loginname")

public static WebElement usernameEditBox;

@CacheLookup

    @FindBy(name = "password")

    public static WebElement passwordEditBox;

@CacheLookup

    @FindBy(name = "btnlogin")

    public static WebElement submitBtn;

@CacheLookup

    @FindBy(xpath = "//span[text()=' AppsOne']")

    public static WebElement verifyLogoinpage;

   

   

public HomePage loginMethod(String username1, String password1) {

new WebDriverWait(driver, 5).until(ExpectedConditions.visibilityOf(usernameEditBox)).sendKeys(username1);

new WebDriverWait(driver, 5).until(ExpectedConditions.visibilityOf(passwordEditBox)).sendKeys(password1);

     new WebDriverWait(driver, 5).until(ExpectedConditions.visibilityOf(submitBtn)).click();


        return new HomePage(driver);


    }

public LoginPage invalidloginMethod(String username2, String password2) {

new WebDriverWait(driver, 5).until(ExpectedConditions.visibilityOf(usernameEditBox)).sendKeys(username2);

new WebDriverWait(driver, 5).until(ExpectedConditions.visibilityOf(passwordEditBox)).sendKeys(password2);

        //driver.findElement(By.id("rememberMe")).click();

new WebDriverWait(driver, 5).until(ExpectedConditions.visibilityOf(submitBtn)).click();


        return this;


    }

public  String verifyLogo() {

        String str =verifyLogoinpage.getText();

        return str;

Simon Stewart

unread,
May 22, 2017, 5:07:02 AM5/22/17
to webdriver
Inline

On Wed, May 10, 2017 at 2:17 AM, sheetal singh <sheet...@gmail.com> wrote:
Hi,

I am curious to know doubts raised by Tony

Its been asked long back, i also have similar doubts


1. If i am using PF model, do i need to use explicit/fluent wait in my test cases. Dont PF automatically take care of all Web element.

No. It simply removes some boiler-plate code when looking elements up. That is:

WebElement element = driver.findElement(By.name("q"));
element.click();

becomes:

element.click();
 
2 In which cases we have to use AjaxElementLocatorFactory initialization.

When you expect the element to loaded through some magic like JS and it might not be present on the page already. This class predates implicit waits and the explicit wait support. Right now, I'd use the normal locator factory and explicit waits.
 
If any one element in page coming through Ajax then do we need to use this?

3. In case i have to use explicit wait in my test cases, then how to get WebElement from a page.
case 1: All WebElements in Page are private, do i need to add getters for all WebElements present inside a Page.


@Test
public void test1(){
    ExpectedConditions.elementToBeClickable(homePage.getWebElement())
}

The elements need not be private. Any field visibility is fine. It's recommended to keep it private just because of good programming style (encapsulation, in this case):

WebElement element = ExpectedConditions.elementToBeClickable(homePage.getWebElement());
 
case 2: In some explicit wait we need By locator to be passed, how to get By locator in case of PF pattern

@Test
public void test1(){
    ExpectedConditions.visibilityOfElementLocated(RequireABylocator)
}

Most of the things supported by ExpectedConditions take either a WebElement or a locator. If you find one that doesn't, then all you need to do is figure out the right locator to use. Unless you've used the @FindBy annotation, this is either By.name or By.id. If you've used the annotation, then life's pretty easy --- it's just there in the code. Alternatively, don't use the PageFactory to initialise that field, and just do the lookup yourself.

Remember, the PageFactory is just there to try and save you some boilerplate. You can always just not use it where it doesn't fit your particular use case.

Cheers,

Simon
 

--
You received this message because you are subscribed to the Google Groups "webdriver" group.
To unsubscribe from this group and stop receiving emails from it, send an email to webdriver+unsubscribe@googlegroups.com.

To post to this group, send email to webd...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages