Wednesday, 21 November 2012

Web Page Object How To

That's been a while since I wrote last time :) Therefore, have lately decided to summarize more or less how to use Page Object properly just to keep the good note for myself.
First of all, let's remember the git of Page Object Pattern briefly. It is an abstract description of a page (web page in our case) or of its part that has got at least one "business value point". Technically, that is a class that has a set of fields and/or their getters/setters which describe the elements available on a page and a set of methods which describe actions you may perform with those elements. And that's it, not less, not more ...

That's the typical page object implementation using webdriver API: (of course you're free to use PageFactory instead of defining the locators as fields)
public class TypicalPageObject {
  private final WebDriver driver;
  
  private static final By ELEMENT_LOCATOR = By.id("element1");
  private static final String VALUE_LOCATOR = "value";

  public TypicalPageObject(WebDriver webdriver) {
    driver = webdriver;
  }

  public void setElementValue(String value) {
    getElement().sendKeys(value);
  }

  public void getElementValue() {
    getElement().getAttribute(VALUE_LOCATOR);
  }

  private WebElement getElement() {
    return driver.findElement(ELEMENT_LOCATOR);
  }
}

Let's analyse it a bit.

  • no public webelements - page object is the margin layer of webdriver API usage; it should not provide any public accessory to others unless it is some child page object
  • WebDriver is defined as constructor parameter - it makes the page object independent from webdriver, though I not entirely agree it should be applied as a pattern - you may initialize driver wherever you want and encapsulate it in page object class. Though, Thucydides throws WrongPageException and says "the page object looks dodgy" if it's constructor does not contain parameter with WebDriver type. That was quite a surprise for me.
  • at least getter - that's the part of "not less" ... you probably may have an element which is read-only
  • no DSL within page object - that's the part of "not more" ... DSL is the higher level of abstraction and by far has nothing to do with page object. Though it is usually implemented invoking the page object. No createUserAccout(), populatePurchaserForm() etc ...
  • No assertions - another part of "not more". Page object should provide the data for them only. 
That's basically it ...
You may also extend the page object with entity based approach. Let's say you have the entity: 

public class TypicalEntity {
    public static final String FIELD = "field";
    private String field;

    public String getField() {
      return field;
    }

    public void setField(String field) {
      this.field = field;
    }
  }

Then our typical page object might look like:

public class TypicalPageObject {
  private final WebDriver driver;
  
  private static final By ELEMENT_LOCATOR = By.id("element1");
  private static final String VALUE_LOCATOR = "value";

  private final Map<String, By> mapLocatorToField = new HashMap<String, By>();

  public TypicalPageObject(WebDriver webdriver) {
    driver = webdriver;
    mapLocatorToField.put(TypicalEntity.FIELD, ELEMENT_LOCATOR);
  }

  public void setElementValue(String field, String value) {
    getElement(field).sendKeys(value);
  }

  public void getElementValue(String field) {
    getElement(field).getAttribute(VALUE_LOCATOR);
  }

  private WebElement getElement(String field) {
    return driver.findElement(mapLocatorToField.get(field));
  }
}
In that case you have to map the accessory to webelements upon the field of an entity. It makes you put additional efforts to maintain entities, though page object becomes more generic.

No comments:

Post a Comment