Cathedral Construction Site

Using Page Objects

The quality of anything you build is going to be a reflection of the underlying infrastructure. This fact applies to temples and test code alike. You wouldn’t build a cathedral on a shoddy foundation, so why would you be willing to build a test suite using fragile coding practices?

Looking down a spiral staircase
Photo By Karl-Ludwig Poggemann

Good coding concepts and practices are not just for use in final applications. Those same ideas should be used when developing tests that will be used to confirm the application’s functionality. I recommend skipping the unit tests of the tests though, you do not want to go down that rabbit hole.

When developing test suites, it is a good idea to utilize encapsulation and abstraction. This will simplify the building of tests while simultaneously increasing code reuse and reducing the fragility of them. One method to achieve this is through the use of page objects.

Let’s Build This Sucker!

Concepts and Blueprints

Most structures have some common features such as foundations, walls, floors, ceilings, and doors. Each of these items has a purpose and certain properties that identify it. By breaking the larger items down into smaller components we render them easier to work with and removes some of the complexity. For example, trying to describe all of the functions of a house is much more difficult than explaining a door and what it does. This concept is the basis for Object Oriented Programming and applies to breaking software into smaller functional pieces. Since test suites are really just an application to verify the functionality of a different application, the concept also applies here. This brings us around to developing Page Objects for use in your Selenium tests.

There are many opinions and methods surrounding the creation and use of page objects but since this is my article you can guess whose version you’re going to get. With that said, I do not recommend using the PageFactory because—in my experience—it becomes redundant and limiting once you move beyond basic functionality. This means we’ll want to build our own objects based on the pages and components we identify in our application.

The first thing you are going to notice when you start looking at your pages is that they have some common features: menus, content areas, etc. Since best practices for coding calls for reducing code duplication, we need to plan a base page object that contains these common bits. We can then create other page objects that extend the base page with functionality specific to the page being tested. Additionally, we can consider breaking out complex components such as menus or WordPress widgets into their own objects to further abstract the functionality.

Let’s outline the blueprints for testing this blog to help firm up the concepts.

  • Base Page
    • Get page title
    • Search for Content
  • Top Menu (page component)
    • Open About The Author
    • Open GitHub
  • Recent Posts (page component)
  • Recent Comments (page component)
  • Archives (page component)
  • Categories (page component)
  • Meta Menu  (page component)
  • Home Page (extends Base Page)
    • All Functions inherited from Base Page
    • Get number of articles displayed

This outline only represents a small portion of the items that could be tested, but it should give you a general idea. I also opted out of doing a login because A) This site doesn’t have a public login. and B) If you look at the additional information links, login pages have been done to death.

Call in the Carpenters and Stonemasons

Hopefully, you’ve followed me so far because this is where it is going to get interesting. It’s time to step out of the theoretical and build some tests. For this example, we will create a simple test that opens the blog, does a search from the home page, and confirms that the search results page is displayed.

For those playing along at home, I am using the following:

  • Eclipse Luna
  • Java 8
  • Selenium 2.45
  • TestNG 6.8.8
  • Maven 3.2.5

All of the code for this test is available as a multiple module Maven project via my Selenium-Examples project on GitHub.

Looking back at our outline, we will need a base page class and a home page class. The other components listed in the outline are not used within the scope of our test, so we won’t worry about adding them. For this test, the base class should include a constructor and a means to check if text is present in a page element. We could also add the search box, since it is a common element for multiple pages, but we will put it in the home page class.

public class BasePage {
 protected WebDriver webDriver;
 
 /**
 * Object constructor
 */
 public BasePage(WebDriver driver) {
 webDriver = driver;
 }

 /**
 * Checks for the specified text within the web 
 * element found using the provided locator Allows for
 * specifying the timeout delay in seconds.
 * 
 * @param locator
 * @param value
 * @param timeout
 * @return boolean
 */
 public boolean isTextPresent(By locator, String value, int timeout) {
 WebDriverWait wait = new WebDriverWait(webDriver, timeout);
 boolean result = false;

 try {
 result = wait.until(ExpectedConditions.textToBePresentInElementLocated(locator, value));
 } catch (TimeoutException toe) {
 result = false;
 }

 return result;
 }

}

The home page will extend the base page and add the necessary functionality. The first pieces to added are element locators for the web elements needed for the test. The element locators will be used rather than WebElement objects to avoid stale element exceptions. In this case, the locators use xPath to locate the correct search field and the page header title. The test will also require a means to use the site, performSearch, and a check method for the page header.

public class WPMain extends BasePage {
 // Declare element locators
 private static final By TXTFLD_SEARCH = By.xpath("//*[@id='search-2']/form/label/input");
 private static final By PAGE_HEADER_TITLE = By.xpath("//*[@id='content']/header/h1[@class='page-title']");

 /**
 * @param driver
 */
 public WPMain(WebDriver driver) {
 super(driver);
 }
 
 /**
 * Check if the specified text is present in the page header
 * @param expectedTitle
 * @return
 */
 public boolean verifyPageHeaderTitle(String expectedTitle) {
 return this.isTextPresent(PAGE_HEADER_TITLE, expectedTitle, 5);
 }
 
 /**
 * Search for the specified test using the search box in the primary side bar.
 * @param term
 */
 public void performSearch(String term) {
 FormElement searchBox = new FormElement(webDriver, TXTFLD_SEARCH);
 searchBox.setValue(term, 5);
 searchBox.submit();
 }

}

* FormElement, used in the above code, is a custom implementation of WebElement that I am developing. It includes some common methods that are frequently used when working with form components. It is included in the example project on GitHub.

With the page functionality defined, we can put the test in place. The test will be self-contained such that it starts the browser session, perform the necessary actions using the page objects, and then ends the session.

public class HomePageSearchTest {
 WebDriver driver = null;
 
 @Test
 public void testHomePagePrimarySidebarSearchBox() {
 // Navigate to the website
 driver.get("http://automation.wolvesbanedesigns.com");
 
 // Instantiate blog page
 WPMain blogPage = new WPMain(driver);
 
 // Perform a test and confirm the correct page is returned
 blogPage.performSearch("Test Automation");
 Assert.assertTrue(blogPage.verifyPageHeaderTitle("Search Results for: Test Automation"), "Incorrect page header displayed.");
 }
 @BeforeMethod
 public void beforeMethod() {
 // Instantiate a new Firefox driver session
 driver = new FirefoxDriver();
 }

 @AfterMethod
 public void afterMethod() {
 // Close the browser session
 driver.quit();
 }

}

Ready for the Groundskeepers

At first glance all of this might seem to be a bit elaborate but a closer inspection shows why it was important to lay a strong foundation. Change is inevitable in all things but even more so with software. As the application changes, it will be necessary to adjust the test harness to keep up. By breaking out the functionality, a change in how a menu works will require only changing the menu class rather than the individual tests that use the menu. If you’ve structured the actual tests correctly, they won’t need to be changed unless the business logic changes. Overall, page objects provide the greatest flexibility and maintainability option for developing tests.

Additional Resources

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s