Browser testing tip: decompose page objects into panel objects

The problem

If you are writing browser-driven tests, the Page Object pattern is a really useful abstraction to use. Let’s assume that our test wants to interact with the navigation bar on a complex page (eg The Guardian homepage):

Guardian homepage

We could create methods for doing this on the page object class:

public class GuardianHomepage {
  [...]

  public SportPage goToTheSport() {
    goTo("sport");
    return new SportPage(driver);
  }

  public MoneyPage goToTheMoney() {
    goTo("money");  
    return new MoneyPage(driver);
  }

  protected void goTo(String section) {
    driver.findElement(By.xpath("//li[@class='" + section + "']/a")).click();
  }
}

However, for a page that has so much going on, putting every access method on the page object class will mean that your page object will have tens of methods on it, making it very hard to comprehend and maintain.

The solution: panel objects

One very neat trick is to break the page object up into panel objects. That really helps reuse as well, because often the same panel appears on several pages.

Thus, your page object would probably look like this:

public class GuardianHomepage {
  [...]

  public NavigationBar navigation() { return new NavigationBar(driver); }
}

public class NavigationBar {
  public NavigationBar(WebDriver driver) {  
    this.driver = driver;  
   }

  public SportPage goToTheSport() {
    goTo("sport");
    return new SportPage(driver);
  }

  public MoneyPage goToTheMoney() {
    goTo("money");
    return new MoneyPage(driver);
  }

  protected void goTo(String section) {
    driver.findElement(By.xpath("//li[@class='" + section + "']/a")).click();
  }
}  

This way, you can easily reuse the NavigationBar class on another part of the site, and in case the markup changes, you only have to change it in one place.

So, once you’ve defined a homepage() method somewhere which loads a WebDriver and launches the homepage, you can use a really nice DSL for navigating to the Sports page in your tests:

SportPage sport = homepage().navigation().goToTheSport()

Thanks to Kenny for prompting me to write this up.