Using the Page Object Design pattern in Selenium Webdriver

In a previous post, we have seen how using an object map significantly reduces the amount of maintenance needed on your Selenium scripts when your application under test is updated. Using this object map principle minimizes duplication of code on an object level. In this post, I will introduce an additional optimization pattern that minimizes code maintenance required on a higher level of abstraction.

Even though we have successfully stored object properties in a SPOM (a Single Point Of Maintenance), we still have to write code that handles these objects every time our script processes a given page including that object in our set of test scripts. If our set of test scripts requires processing a login form five times throughout the execution, we will need to include the code that handles the objects required to log in – a username field, a password field and a submit button, for example – five times as well. If the login page changes but the objects defined previously remain the same – for example, an extra checkbox is included to have a user agree to certain terms and conditions – we still need to update our scripts five times to include the processing of the checkbox.

To eliminate this code redundancy and maintenance burden, we are going to use a different approach known as the Page Object design pattern. This pattern uses page objects that represent a web page (or a form within a page, if applicable) to separate test code (validations and test flow logic, for example) from page specific code. It does so by making all actions that can be performed on a page available as methods of the page object representing that page.

So, assuming our test scripts needs to login twice (with different credentials), instead of this code:

public static void main(String args[]) {
	
	// start testing
	WebDriver driver = new HtmlUnitDriver();
		
	// first login
	driver.get("http://ourloginpage");
	driver.findElement(objMap.getLocator("loginUsername")).sendKeys("user1");
	driver.findElement(objMap.getLocator("loginPassword")).sendKeys("pass1");
	driver.findElement(objMap.getLocator("loginSubmitbutton")).click();
		
	// do stuff
		
	// second login
	driver.get("http://ourloginpage");
	driver.findElement(objMap.getLocator("loginUsername")).sendKeys("user2");
	driver.findElement(objMap.getLocator("loginPassword")).sendKeys("pass2");
	driver.findElement(objMap.getLocator("loginSubmitbutton")).click();
		
	// do more stuff
	
	// stop testing
	driver.close();
}

we would get

public static void main(String args[]) {
		
	// start testing
	WebDriver driver = new HtmlUnitDriver();
		
	// first login
	LoginPage lp = new LoginPage(driver);
	HomePage hp = lp.login("user1","pass1");
		
	// do stuff
		
	// second login
	LoginPage lp = new LoginPage(driver);
	HomePage hp = lp.login("user2","pass2");
		
	// do more stuff
		
	// stop testing
	driver.close();
}

Now, when we want to go to and handle our login page, we simply create a new instance of that page and call the login method to perform our login action. This method in turn returns a HomePage object, which is a representation of the page we get after a successful login action. A sample implementation of our LoginPage object could look as follows:

public class LoginPage {
	
	private final WebDriver driver;
	
	public LoginPage(WebDriver driver) {
		this.driver = driver;
		
		if(!driver.getTitle().equals("Login page")) {
			// we are not at the login page, go there
			driver.get("http://ourloginpage");
		}
	}
	
	public HomePage login(String username, String password) {
		driver.findElement(objMap.getLocator("loginUsername")).sendKeys("username");
		driver.findElement(objMap.getLocator("loginPassword")).sendKeys("password");
		driver.findElement(objMap.getLocator("loginSubmitbutton")).click();
		return new HomePage(driver);
	}	
}

It contains a constructor that opens the login page if it is not visible already. Alternatively, you could throw an exception and stop test execution whenever the login page is not the current page, depending on how you want your test to behave. Our LoginPage class also contains a login method that handles our login actions. If ever the login screen changes, we only need to update our test script once thanks to the proper use of page objects.

When the login action is completed successfully, our test returns a HomePage object. This class will be set up similar to the LoginPage class and provide methods specific to the page of our application under test it represents.

In case we also want to test an unsuccessful login, we simply add a method to our LoginPage class that executes the behaviour required:

public LoginPage incompleteLogin(String username) {
	driver.findElement(objMap.getLocator("loginUsername")).sendKeys("username");
	driver.findElement(objMap.getLocator("loginSubmitbutton")).click();
	return this;
}

This alternative login procedure does not enter a password. As a result, the user is not logged in and the login page remains visible, hence we return the current LoginPage object here instead of a HomePage object. If we want to test this type of incorrect login in our script, we simply call our new incorrectLogin method:

public static void main(String args[]) {
		
	// start testing
	WebDriver driver = new HtmlUnitDriver();
		
	// incorrect login
	LoginPage lp = new LoginPage(driver);
	lp = lp.incompleteLogin("user1");
	Assert.assertEquals("You forgot to type your password",lp.getError());
		
	//stop testing
	driver.quit();
}

The getError method is implemented in our LoginPage class as well:

public String getError() {
	return driver.findElement(objMap.getLocator("errorField")).getText();
}

This getError method is the result of another best practice. In order to keep your test code as much separated from your object code, always place your assertions outside of your page objects. If you need to validate specific values from a page, write methods that return them, as we did in the example above using the getError method.

To wrap things up, using the Page Object design pattern, we introduced another Single Point of Maintenance or SPOM in our Selenium test framework. This means even less maintenance required and higher ROI achieved!

An example Eclipse project using the pattern described above can be downloaded here.

20 thoughts on “Using the Page Object Design pattern in Selenium Webdriver

  1. Hi bas,
    The below code which you have written:
    // first login
    LoginPage lp = new LoginPage(driver);
    HomePage hp = lp.login(“user1″,”pass1”);

    // do stuff

    // second login
    LoginPage lp = new LoginPage(driver);
    HomePage hp = lp.login(“user2″,”pass2”);

    // do more stuff
    still contains LoginPage lp = new LoginPage(driver); two times right?

    • Hi Sherin,

      Yes, that line is used to create the starting conditions for a login test. There are two login tests performed, so we need to open the login page twice.

  2. Hi Bas,
    I am bharath i learnt manual testing and it takes more time to complete and i want to start automation as beginner and i want like minimum usage of code for textboxs,linkbuttons,navigation of pages,dropdownlist items,images and so many controls.I hope you will and your informations will help me to improve my knowledge and Thank you

  3. Hi Bass,

    I have a bunch of doubts.

    1)Is it possible to set explicit wait in page object model?
    and How to achieve this with current example?

    2)I want to set explicit wait with HomePage element.
    How can i access elements of Home page in Login Page?

    3)what about inheritance/static method? is it a good approach?

    • Hey Jithin,

      1) Sure. My personal preference is to use wrapper methods around the default Selenium methods for click(), sendKeys(), etc. Have a look at this post to see what I mean. The wrapper methods use explicit waits and are highly reusable.

      2) Why would you want to access elements of the HomePage object in methods in the LoginPage object in the first place? If they’re the same objects that are visible on both pages (for example a page footer that appears oneveyr page), it might be better to extract them into their own page object. Remember that a Page Object does not need to relate 1-to-1 with an actual page.

      3) What kind of methods would you like to make static?

      • Bas, thanks for your reply. I will try things with wrapper method

        Actually i have created login method with like this. Accessed webelement from HomePage through inheritance.

        public Class LoginPage extends HomePage

        login_into_app(){

        User_nameField.sendKeys(“username”);
        PasswordField.sendKeys(“password”);
        SubmitBtn.click();

        //here defined explicit wait along with Home Page element(wait until home page element is clickable)

        }

        I don’t want to set explicit wait in Test case. How can i achieve this if it is wrong?

        • First of all, why does your LoginPage extend your HomePage? Aren’t they two different pages?

          What I usually do is add a load() method to each Page Object where I put the logic to determine whether a given page is loaded. In this, I use explicit waits, so they don’t have to be written in the test methods. Even better is to use the LoadableComponent pattern for this. See this blog post for an example.

          • After login operation i am doing some operations in home page.

            At the time of execution it facing ‘NoSuchElement’ exception. For avoiding that i used inheritance and defined explicit wait in the login() function.

            Currently i am trying with your suggestions.

  4. Hi Bas,

    That’s a very good article. I am currently giving interviews for Automation Engineer Role, since you are an experienced automation engineer, i have couple of questions, if you can help me with that.Do you mind if i can send you an email?

    Thanks

Leave a Reply

Your email address will not be published. Required fields are marked *