Learn Page Objects in 3 Easy Lessons




How does your test automation code look like?



Does it look like this?


driver.get(homePageUrl);

WebElement searchTextBox = browserDriver.findElement(By.id(searchTextBoxId));

searchTextBox.click();

searchTextBox.clear();

searchTextBox.sendKeys(keyword);

WebElement searchTextButton = driver.findElement(By.id(searchTextButtonLocator));

searchTextButton.click();

assertTrue(driver.getTitle().equalsIgnoreCase(expectedResultsPageTitle));

assertTrue(driver.getCurrentUrl().equalsIgnoreCase(expectedResultsPageUrl));

WebElement resultCountLabel = browserDriver.findElement(By.xpath(resultCountLocator);

String resultCountText = resultCountLabel.getText();

int startIndex = resultCountText.substring("of") + 3;

int endIndex = resultCountText.substring(" items");

int resultCount = Integer.parseInt(resultCountText.substring(startIndex, endIndex);

assertTrue(resultCount > 0);


or this?

HomePage homePage = new HomePage(driver);

homePage.openPage();

homePage.searchForKeyword("java");

ResultsPage resultsPage = new ResultsPage(driver);

assertTrue(resultsPage.isOpen() == true);

assertTrue(resultsPage.resultCount() > 0);



If you can write the first version and want to modify it into the second, this step-by-step tutorial is for you.

The tutorial shows you everything you need to know for creating page objects.


The tutorial has 3 parts:

1. how to create page objects

2. how to create page elements

3. how to use page factory



When learning to create page objects, we will go through the following iterations:

1. PREPARE THE TEST CASE FOR AUTOMATION

1.1 group test case actions by page
1.2 break down test case actions in sub-actions
1.3 create methods and variables for each action and sub-action 

2. CREATE THE TEST CLASS

  2.1 Component of a test class 
2.2 How a test script is executed 
2.3 Create the template of the test class 
2.4 Add code for setUpEnvironment() and cleanUpEnvironment() methods 
2.5 Add the code for the test script 
2.6 Create objects for the homePage and resultsPage 

3. CREATE THE HOME PAGE CLASS 

3.1 Create the template of the home page 
3.2 Add the fields and methods to the class 
3.3 Add values to the fields of the class 
3.4 Add the WebDriver field and constructor to the class 
3.5 Add the code for the methods 

4. CREATE THE RESULTS PAGE CLASS 

4.1 Create the template of the results page
4.2 Add the fields and methods to the class 
4.3 Add values to the fields of the class 
4.4 Add the WebDriver field and constructor to the class 
4.5 Add the code for the methods 



Want to get started?


Transfer USD 25 through Paypal to alex@alexsiminiuc.com today.

You get the 3-part tutorial and unlimited email support.

Have too Many Assertions in Test Scripts? Use Custom Assertions Instead




If your JUNIT test script uses too many assertions, you can reduce their number with
  • custom assertions  and 
  • test objects

Having less assertions in the test script makes the script shorter, easier to read and maintain.

These are things that need to be done on all code, both for application and tests.



Lets start with an example

The following code sample tests that multiple user variables (firstName, lastName, age, address, city, country) have the correct values.



import static org.junit.Assert.*;

import org.junit.Test;

public class AssertionTests {

private String generateFirstName()   
{ return "John";   }

private String generateLastName()    
{  return "Smith";  }

private int generateAge()            
{  return 32;       }

private String generateAddress()     
{  return "123 main street";  }

private String generateCity()        
{  return "Burnaby";   }

private String generateCountry()     
{  return "USA";   }

@Test
public void testUserDetailsWithMultipleAssertions()
{

String firstName = getFirstName();


String lastName = getLastName();

int age = getAge();

String address = getAddress();

String city = getCity();

String country = getCountry();

assertTrue("firstname is incorrect", 
firstName.equalsIgnoreCase("John"));

assertTrue("lastname is incorrect",  
lastName.equalsIgnoreCase("Smith"));

assertEquals("age is incorrect", age, 32);

assertTrue("address is incorrect", 
address.equalsIgnoreCase("123 main street"));

assertTrue("city is incorrect", 
city.equalsIgnoreCase("Burnaby"));

assertTrue("country is incorrect", 
country.equalsIgnoreCase("USA"));

}



The user variables' values are provided by utility methods (getFirstName(), getLastName(), getAge(), getAddress(), getCity()).

The test script works but it is too long and complicated for the simple thing that it implements.



Let's assume that it is possible to get a User object instead of getting firstName, lastName, age, address and city separately.

The user object is provided by a utility method called getUser().

It returns an object of  a User class:



public class User {

private String firstName;

private String lastName;

private int age;

private String address;

private String city;

private String country;

public User(String firstName, String lastName, int age, 
String address, String city, String country)
{

this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.address = address;
this.city = city;
this.country = country;

}

public String firstName() {
return this.firstName;
}

public String lastName() {
return this.lastName;
}

public int age() {
return this.age;
}

public String address() {
return this.address;
}

public String city() {
return this.city;
}

public String country() {
return this.country;
}

}


The User class is very simple:
  • The constructor has parameters for firstName, lastName, age, address and city and saves them in the class's fields
  • The class has get methods for its fields

Having the User class, we can use an object in the test script instead of separate variables:


import static org.junit.Assert.*;

import org.junit.Test;

public class AssertionTests {

private User getUser()
{

return new User("John", "Smith", 32, "123 main street", "Burnaby", "USA"

}

@Test
public void testUserDetailsWithMultipleAssertions()
{

User user = getUser();

assertTrue("firstname is incorrect",
user.firstName().equalsIgnoreCase("John"));

assertTrue("lastname is incorrect",
user.lastName().equalsIgnoreCase("Smith"));

assertEquals("age is incorrect",
user.age(), 32);

assertTrue("address is incorrect",
user.address().equalsIgnoreCase("123 main street"));

assertTrue("city is incorrect",
user.city().equalsIgnoreCase("Burnaby"));

assertTrue("country is incorrect",
user.country().equalsIgnoreCase("USA"));

} }




The test script looks a bit better when using the user object but it is still too long.



Create a custom assertion



The first way of simplifying the test script is with a custom assertion method.

Since we are using the user object, we can pass it to the custom assertion who will just include the individual assertions:




import static org.junit.Assert.*;

import org.junit.Test;

public class AssertionTests {

private void assertCorrectUserDetails(User user)
{

assertTrue("firstname is incorrect",
user.firstName().equalsIgnoreCase("John"));

assertTrue("lastname is incorrect",
user.lastName().equalsIgnoreCase("Smith"));

assertEquals("age is incorrect", user.age(), 32);

assertTrue("address is incorrect",
user.address().equalsIgnoreCase("123 main street"));

assertTrue("city is incorrect",
user.city().equalsIgnoreCase("Burnaby"));

assertTrue("country is incorrect",
user.country().equalsIgnoreCase("USA"));

}

private User getUser()
{

return new User("John", "Smith", 32, "123 main street", "Burnaby", "USA");

}

@Test
public void testUserDetailsWithCustomAssertion()
{

User user = getUser();

assertCorrectUserDetails(user);

}

}



The test script is very short when using the user object and a custom assertion.

But it lacks clarity since there is data hard-coded in the assertions that make the custom assertion.

We can remove the hard-coded data by creating a test object that uses the User class as well.

Then, we compare the user object with the test object in an assertion.




Compare the user object with a test user object


First, we will add a new method to the User class that compares 2 user objects:


public Boolean equalsTo(User anotherUser)
{

Boolean result = false;

if (
this.firstName().equalsIgnoreCase(anotherUser.firstName()) &&
this.lastName().equalsIgnoreCase(anotherUser.lastName()) &&
this.age() == anotherUser.age() &&
this.address().equalsIgnoreCase(anotherUser.address()) &&
this.city().equalsIgnoreCase(anotherUser.city()) &&
this.country().equalsIgnoreCase(anotherUser.country())
)

result = true;


return result;

}



The method is quite simple.

It gets a User object as parameter and compares the parameter's fields with the currect object's fields.

If all fields are equal, it returns true, otherwise false.

The test script gets its final version now:


@Test
public void testCorrectUser()
{

User user = getUser();

User testUser = new User("John", "Smith", 32, "123 main street", "Burnaby", "USA");

assertTrue(user.equalsTo(testUser) == true);

}




It first gets the user object.

Then, it creates a testUser object.

Finally, it asserts that the 2 objects are equal using the equalsTo() method of the User class.