Selenium - Page Object Pattern

Published Friday, October 14, 2011 5:37 PM

 

After bitching and moaning about the instability of Selenium, after searching high and low for alternatives, I am back to Selenium again, convinced it is still the best in town. Yep, it is flexible, it is free, it is being actively developed therefore improved by day. I can write my unit tests in C# or any other server languages. And it is getting more and more backings from major vendors. I can even take my tests to the Cloud, e.g., the SauceLab. 

Enough of justifications. 

Selenium Page Object Pattern

One of the test designs in Selenium that has gained more and more popularity is Page Object Pattern, the core of it is to wrap your page into classes with members and methods, and in testing. Then tests can call the methods to initiate any interactions with the page UI, the way normal users does, login, run report, click a button, etc. It is a OOO approach, and its advantages are:

  •  
    • Clean separation between test code and page specific code. 
    • Single repository for the services or operations offered by individual page object. 

So take an example of login, a normal selenium login test would be written as the following:

 

/***
 * Tests login feature
 */
public class Login {

        public void testLogin() {
                selenium.type("inputBox", "testUser");
                selenium.type("password", "my supersecret password");
                selenium.click("sign-in");
                selenium.waitForPageToLoad("PageWaitPeriod");
                Assert.assertTrue(selenium.isElementPresent("compose button"),
                                "Login was unsuccessful");
        }
}

Now with Page oject pattern design in mind, we would wrap the login page in its own object, 

/**
 * Page Object encapsulates the Sign-in page.
 */
public class SignInPage {

        private Selenium selenium;

        public SignInPage(Selenium selenium) {
                this.selenium = selenium;
                if(!selenium.getTitle().equals("Sign in page")) {
                        throw new IllegalStateException("This is not sign in page, current page is: "
                                        +selenium.getLocation());
                }
        }

        /**
         * Login as valid user
         *
         * @param userName
         * @param password
         * @return HomePage object
         */
        public HomePage loginValidUser(String userName, String password) {
                selenium.type("usernamefield", userName);
                selenium.type("passwordfield", password);
                selenium.click("sign-in");
                selenium.waitForPageToLoad("waitPeriod");

                return new HomePage(selenium);
        }
}

And the new login test would be:

/***
 * Tests login feature
 */
public class TestLogin {

        public void testLogin() {
                SignInPage signInPage = new SignInPage(selenium);
                HomePage homePage = signInPage.loginValidUser("userName", "password");
                Assert.assertTrue(selenium.isElementPresent("compose button"),
                                "Login was unsuccessful");
        }
}

So now we get a suite of page objects with clearly defined methods and services and tests can issue their tests by calling services with appropriate parameters. The code is cleaner and more readable. 

Some principles of Page Oject Pattern:

 

  • Page obect itself should never make any assertions or verification. This is the job of a test.
  • Page object, however, upon its initiation, should ensure all elements are loaded. 
  • A page object does not need to represent an entire page, but only to capture the critical components and services that are to be tested.

 

 

See Also:

Test Design Considerations

by xxxd
Filed under:

Comments

No Comments

This site

This Blog

Syndication

Sponsors

  • MaximumASP