As a developer of automated tests for web pages one always has to deal with often changes on a site. This also means constant changes in test cases too. The developers’ main goal is to make as high quality code as possible with the least investment. This is also true for tests changes. Both goals can be reached with using the correct tool. The Page Object pattern hand in hand with Page Factory could provide a healthy code and maintainable tests.
Page Object is one of the most popular patterns which is used for developing automated web test. It maps the pages to objects which contains all the required data and functions that help the developer to test the site. The fields contain all the information what is needed and the methods do all the job to navigate in pages, fill the inputs or click the buttons.
Web driver provides more way to grab the element from the site. All of them are based on search in DOM tree of the webpage based on locators. The locators are strings that describe the position of the required web element. It can be located based on its name, id or src attributes or any custom conditions like CSS or Xpath selector. Let’s take a look for an example. The test case is really simple. Log in to Gmail and check the title of the next site. If the page title contains word Inbox, it’s done. Otherwise something went wrong.

public class LoginTest {
	
	WebDriver driver;

	@Test
	public void testSuccessfulLogin(){
		driver = new FirefoxDriver();
		driver.get("https://accounts.google.com/ServiceLogin?...");
		
		GoogleLoginPage googleLoginPage = new GoogleLoginPage(driver);
		googleLoginPage.fillUsername("testMail@gmail.com");
		googleLoginPage.fillPassword("********");
		MailPage mailPage = googleLoginPage.clickLoginButton();
		Assert.assertTrue("This is not the inbox page", mailPage.getPageTitle().contains("Inbox"));
		
	}

}

In the above example two page objects were used namely GoogleLoginPage and MailPage. GoogleLoginPage gives a model of the login page. It contains three web elements and three functions which make the login process possible. The class looks like:

public class GoogleLoginPage {
		
	WebDriver driver;
	
	private WebElement usernameWebelement;
	private WebElement passwordWebElement;
	private WebElement loginButton;
	
	public GoogleLoginPage(WebDriver driver) {
		this.driver = driver; 
	}
	
	public void fillUsername(String username){
		usernameWebelement = driver.findElement(By.cssSelector("#Email"));
		usernameWebelement.sendKeys(username);
	}
	
	public void fillPassword(String password){
		passwordWebElement = driver.findElement(By.cssSelector("#Passwd"));
		passwordWebElement.sendKeys(password);
	}
	
	public MailPage clickLoginButton(){
		loginButton = driver.findElement(By.cssSelector("#signIn"));
		loginButton.click();
		return new MailPage(driver);
	}
	
}

The web elements are initialized in appropriate methods with a CSS selector. The methods send the strings and click the button. However clickLoginButton has a MailPage return type. The call of this function initialize the new page in the return progress. This is why page object pattern is popular. Thanks to this step the developer always knows where they currently are in the test process and only those functions are available which are actually needed to be used in the given situation. The pattern forces the tester to create page objects to new pages and this costs time but it is absolutely worth it because they got high quality code, which is much easier to maintain.
MailPage contains only a getPageTitle method which gets the title from driver.

public class MailPage {
		
	WebDriver driver;
	
	public MailPage(WebDriver driver) {
		this.driver = driver; 
	}
	
	public String getPageTitle(){
		return driver.getTitle();
	}
	
}

This method is required for checking the condition in the assertion. This verifies the page title, and if it contains word “Inbox”, which is normal way of run, the test is completed. Otherwise it will throw an assertion which says: This is not the inbox page. The general way of assertion usage is to put them into test classes and not in page classes expect if specific verification that check if the page is loaded and similar cases.
It needs to highlight that the page object does not necessarily represent a whole page, it can only be just a part of it.
Although the web driver waits are better and better, it is often recommended to use one’s own wait in some situations. For instance in AJAX heavy web pages it is much easier to navigate if the next move waits the AJAX’s end.

public void waitForAjaxToFinish() {
		int timeout = 0;
		while(timeout<400) {
			boolean ajaxWorking = (boolean) ((JavascriptExecutor) driver)
					.executeScript("return jQuery.active == 0");
			if(ajaxWorking) {
				break;
			}
			try{
				timeout++;
				Thread.sleep(500);
			} catch(Exception e) {

			}
		}
	}

This example waits 20 seconds for AJAX to finish its work.

Page Factory makes the web element location and initialization much easier. The GoogleLoginPage class looks like:

public class GoogleLoginPage {
			
	WebDriver driver;
	
	@FindBy(css = "#Email")
	private WebElement usernameWebelement;
	
	@FindBy(css = "#Passwd")
	private WebElement passwordWebElement;
	
	@FindBy(css = "#signIn")
	private WebElement loginButton;
	
	public GoogleLoginPage(WebDriver driver) {
		this.driver = driver; 
	}
	
	public void fillUsername(String username){
		usernameWebelement.sendKeys(username);
	}
	
	public void fillPassword(String password){
		passwordWebElement.sendKeys(password);
	}
	
	public MailPage clickLoginButton(){
		loginButton.click();
		return PageFactory.initElements(driver, MailPage.class);
	}
	
}

Web elements are evaluated lazily which means that if the web element is not used its @Findby will never be called. The @Findby annotation proves many ways to grab the web elements. If the annotation is not present the web driver will still try to locate the element. It will search for id and name attributes and if it matches the element will be allocated. Otherwise CSS and Xpath could be used for locating. For more options check the official documentation (https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/FindBy.html).
The page object creation is not the same as before. In that case PageFactory will perform the creation as it is shown in clickLoginButton method. Similarly changed the test also:

GoogleLoginPage googleLoginPage = PageFactory.initElements(driver, GoogleLoginPage.class);

The advantages of Page factory with the Page Object pattern are that the code duplication could be avoided and the tests are more readable and robust. Separating test specification from test implementation improves the maintainability of tests and makes them more proof against frequent changes which is very useful in agile methodology.

Similar Posts from the author: