Category Archives: 09. Make your Tests Maintainable

Page Object Model – Introduction

Creating Selenium test cases can result in an unmaintainable project. One of the reasons is that too many duplicated code is used. Duplicated code could be caused by duplicated functionality and this will result in duplicated usage of locators. The disadvantage of duplicated code is that the project is less maintainable. If some locator will change, you have to walk through the whole test code to adjust locators where necessary. By using the page object model we can make non-brittle test code and reduce or eliminate duplicate test code. Beside of that it improves the readability and allow us to create interactive documentation. Last but not least, we can create tests with less keystrokes. An implementation of the page object model can be achieved by separating the abstraction of the test object and the test scripts.

This section of the blog will explain the common use of the page object model. We will make use of an Integrate Development Environment, called Eclipse.

Set up a Selenium project environment

Problem

This recipe will describe how we can set up our project environment. We will use Eclipse as development environment as further examples will be written in Java, but we can use any IDE which support our favorite programming language, preferable a strong type language. (like: ActionScript 3, C++, C#, Java, Python, OCaml, Vala) A strong type language sets the boundaries where we have to behave in, other than a dynamic type language(like: BASIC, JavaScript, Perl, PHP, Rexx).

Prerequisites

Make sure you have download the latest stable TestNG library from http://www.testng.org. Next thing is to make sure that we have the latest stable version of the Selenium library from http://code.google.com/p/selenium/downloads/list.

Solution

  1. Create a new Java project in Eclipse
  2. Create two separate folders, named tests and src
  3. Import the TestNG library. By opening the context menu on the project, and select Properties > Java Build Path > Libraries > Add External JARs
  4. Import the Selenium library in the same way as described above

What has been done

We have create our project structure, by importing the libraries and creating two separate folders. In the src folder we will create the website abstraction and in the tests folder we will create our test scripts.

Tip 1: The latest Selenium releases comes with a lot of additional libraries which are very useful and can be found in the libs folder.

Model the application interface

The first step we have to take in implementing the page object model is that we have to model the user experience. This means that all page specific elements has to be extracted to separate classes. This will guarantee that all functionality will be scripted only once.

Getting ready
The tests drive the implementation of the page object. In this way, we never end up with unused page object code.

How to do it…

  1. Create a new class file and refer the name to the actual page from the test object, by opening the context menu on the src folder and select New > Class. Make sure you fill in a package name and class name. Package name should refer to the entire website and the class name should refer to the specific page.
  2. Import the Selenium package in order to use the functions in the API. The code will look like this:
package prestashop;

import org.openqa.selenium.*;

public class SearchPage {

}

Expose methods

We can expose methods in order to reduce duplicated code. We are able to call the method multiple times. This will ensure a better maintainable test code, because we only have to make adjustments and improvements in one particular place.

**Getting ready
**Search for duplicated functionality we use in our tests. For example the ‘login’ functionality. The selenium actions we have to provide to login will look like this:

WebElement emailEl = driver.findElement(By.name("email"));
emailEl.sendKeys("test@test.com");
WebElement passwordEl = driver.findElement(By.name("password"));
passwordEl.sendKeys("1qazxsw2");
WebElement loginForm = driver.findElement(By.name("login"));
loginForm.submit();

 

How to do it…
We can simple wrap the described functionality in a method and we can give it a sensible name. We can create the method like this:

public void login() {
        WebElement emailEl = driver.findElement(By.name("email"));
        emailEl.sendKeys("test@test.com");
        WebElement passwordEl = driver.findElement(By.name("password"));
        passwordEl.sendKeys("1qazxsw2");
        WebElement loginForm = driver.findElement(By.name("login"));
        loginForm.submit();
    }

We can call this function using the following code:

login();

How it works…
We can simple call the method from our test script, once we have to add one item to the cart. The sensible method name tells use directly what the selenium API calls are doing.

There’s more…

Passing parameters through methods
We can pass parameters through methods, just as in normal programming code. The code below will show us how we can login with parameterized email and password.

public void login(String email, String password) {
        WebElement emailEl = driver.findElement(By.name("email"));
        emailEl.sendKeys(email);
        WebElement passwordEl = driver.findElement(By.name("password"));
        passwordEl.sendKeys(password);
        WebElement loginForm = driver.findElement(By.name("login"));
        loginForm.submit();
    }

We can call this function using the following code:

login("test@test.com", "1qazxsw2");

Dealing with moving focus

Problem

Imagine the following situation: after accepting a confirmation dialog you will be redirected to another page. This recipe will explain how to deal with this inevitable situation.

Prerequisites

We have made a class file for every unique page. So in theory every page is accessible.

How to do it…

We have to change the return-type of the method, in this case we set it to MemberPage. The doLogin method will look like this:


public MemberPage doLogin(String email, String password) {
    WebElement emailEl = driver.findElement(By.name("email"));
    emailEl.sendKeys(email);
    WebElement passwordEl = driver.findElement(By.name("password"));
    passwordEl.sendKeys(password);
    WebElement loginForm = driver.findElement(By.name("login"));
    loginForm.submit();
    return new MemberPage();
}
1
2
3
4
5
6
7
8
9
public MemberPage doLogin(String email, String password) {
    WebElement emailEl = driver.findElement(By.name("email"));
    emailEl.sendKeys(email);
    WebElement passwordEl = driver.findElement(By.name("password"));
    passwordEl.sendKeys(password);
    WebElement loginForm = driver.findElement(By.name("login"));
    loginForm.submit();
    return new MemberPage();
}

How it works…

The MemberPage object is returned, once we call the doLogin method. From our testscript perspective we can access all the public methods in the MemberPage class.

Ambiguous focus

Set the return-type to void, if the focus of an certain action is ambiguous. Like in highly dynamic websites, where we have no control on the input/output data. In this case we can use the code below:


public void doLogin(String email, String password) {
    WebElement emailEl = driver.findElement(By.name("email"));
    emailEl.sendKeys(email);
    WebElement passwordEl = driver.findElement(By.name("password"));
    passwordEl.sendKeys(password);
    WebElement loginForm = driver.findElement(By.name("login"));
    loginForm.submit();
}
1
2
3
4
5
6
7
8
public void doLogin(String email, String password) {
    WebElement emailEl = driver.findElement(By.name("email"));
    emailEl.sendKeys(email);
    WebElement passwordEl = driver.findElement(By.name("password"));
    passwordEl.sendKeys(password);
    WebElement loginForm = driver.findElement(By.name("login"));
    loginForm.submit();
}