Selenium 101: Mastering Selenium Design Patterns
  March 22, 2018

In all prior Selenium 101 posts, we used rather small and easily contained examples to implement our Selenium test cases. Unfortunately, this does not represent the real world usage that well.

To get better and more maintainable tests we will need to come up with a better structure for our tests. The most commonly used design patterns are the Page Object Model and the in-build Page Factory Pattern. In this post, we will take a look at both.

Page Object Model

By using the Page Object Model you will create a separate class for each page you need to interact with during a test. These objects are then reused in every test case that needs to interact with the specific page. This has a few different advantages:

Code Reuse: You can reuse the page specific class in every test and don’t need to copy and paste any page specific code.

Maintainability: If a page changes you only need to apply the necessary changes to one class and not in every test case separately.

Readability: By using descriptive names in the page object you get much more readable test cases.

As an example, we will take a simple login page that contains 3 different elements: A username input box, a password field, and a button to submit the login.

<!DOCTYPE html>

<html>

<head>

 <title>Selenium Academy Design Patter</title>

</head>

<body>

 <form>

 <div>

<h3>Login</h3>

 </div>

<div>

<input type="text" id="username"></input>

 </div>

<div>

<input type="password" id="password"></input>

 </div>

<div>

<input type="submit" id="login" value="Login"></input>

 </div>

</form>

 </body>

</html>

To create a test for this page using the Page Object Model we first need to create the necessary class specific to this page. Most likely our test will need to access the username and password fields and, in addition to this, we will need a method to actually submit the login. The page object model ‘LoginPage’ could look like this:

import org.openqa.selenium.By;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

public class LoginPage {

private final WebDriver driver;

private final By username = By.id("username");

private final By password = By.id("password");

private final By login = By.id("login");

public LoginPage(WebDriver driver) {

this.driver = driver;

}

public String GetUsername() {

return driver.findElement(username).getAttribute("value");

}

public void setUsername(String value) {

WebElement element = driver.findElement(username);

element.clear();

element.sendKeys(value);

}

public String getPassword() {

return driver.findElement(password).getAttribute("value");

}

public void setPassword(String value) {

WebElement element = driver.findElement(password);

element.clear();

element.sendKeys(value);

}

public void submitLogin() {

driver.findElement(login).click();

}

}

Now we can use this page object in our test case. Please note that here the test case does not need to know any specifics of the login page at all. A test could look like this:

import org.junit.Test;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.chrome.ChromeDriver;

public class LoginTest {

@Test

public void testLogin() {

WebDriver driver = new ChromeDriver();

driver.navigate().to("http://www.selenium.academy/Examples/DesignPattern.html");

// Create a new instance of the login page object

LoginPage login = new LoginPage(driver);

// set the username

login.setUsername("daniel");

// set the password

login.setPassword("secret");

// submit the login

login.submitLogin();

driver.quit();

}

}

Page Factory

As you have just seen it is possible to implement the page object model without any support from Selenium. Luckily Selenium does offer the possibility to create Page Objects by using the Selenium integrated Page Factory. With the Selenium Page Factory, you can make your page objects even shorter and you will get a performance improvement as Selenium handles most of the heavy lifting.

Let us take a look at how we can modify our Login Page object from above to use the Selenium Page Factory pattern. Please note that with the code below Selenium will cache the lookup of all elements. You would only do this if you are certain that the elements do not change during the lifetime of the page object. On an AJAX-heavy application, you should avoid this and have Selenium lookup the element each time it is accessed.

import org.openqa.selenium.WebElement;

import org.openqa.selenium.support.CacheLookup;

import org.openqa.selenium.support.FindBy;

import org.openqa.selenium.support.How;

public class LoginPage {

@FindBy(how = How.ID, using = "username")

@CacheLookup

private WebElement username;

@FindBy(how = How.ID, using = "password")

@CacheLookup

private WebElement password;

@FindBy(how = How.ID, using = "login")

@CacheLookup

private WebElement login;

public String GetUsername() {

return username.getAttribute("value");>/pre>

}

public void setUsername(String value) {

username.clear();

username.sendKeys(value);

}

public String getPassword() {

return password.getAttribute("value");

}

public void setPassword(String value) {

password.clear();

password.sendKeys(value);

}

public void submitLogin() {

login.click();

}

}

Of course, our test cases will also need a small improvement to properly use the Page Factory:

import org.junit.Test;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.chrome.ChromeDriver;

import org.openqa.selenium.support.PageFactory;

public class LoginTest {

@Test

public void testLogin() {

WebDriver driver = new ChromeDriver();

driver.navigate().to("http://www.selenium.academy/Examples/DesignPattern.html");

// Create a new instance of the login page object

LoginPage login = PageFactory.initElements(driver, LoginPage.class);

// set the username

login.setUsername("daniel");

// set the password

login.setPassword("secret");

// submit the login

login.submitLogin();

driver.quit();

}

}

And just like that, you can create readable and maintainable Selenium tests with the help of Page Factory and/or the Page Object Pattern.

Want to know more about Selenium? Check out the full course with 30+ lessons and screencasts over at selenium.academy.

You can also browse the rest of our Selenium 101 Series: