Sunday, December 18, 2011

Jbehave web and page object pattern

Objective

To explain  page object pattern and how to use it with jbehave web(3.4.3) and WebDrive.
A sample project is uploaded in github. It is simple enough to use as a startup project.
The implementation was done following this article. The basic is explained there, so in my post I will discuss about the page object pattern, which simplifies the web drive implementation.

Page object pattern

Each page has a one to one mapping with a java class
This might seem to be obvious to map all the pages with a java class. And the navigation from one page to another to map to a method of one object that returns the navigation page object. This approach increases readability in a great extant. Now look at a sample code(just to get an idea).
public class BasicWebSteps {

    private final Pages pages;

    public BasicWebSteps(Pages pages) {
        this.pages = pages;
    }

    @Given("user is on Home page")
    public void userIsOnHomePage(){        
        pages.homePage().open();        
    }

    @When("user clicks on Resume from menu")
    public void userClicksOnResumeLink(){        
        pages.resumePage().open();
    }

    @Then("Resume page is shown")
    public void runStoryPageIsShown(){
        pages.resumePage().assertShown();
    }

}
This increases the readability of the step definition. It encapsulates the logic of navigation, structure of page specific html,  page specific assertions for each pages in corresponding classes. This approach follows Single Responsibility Principle and makes a logical separation in the test project.
Jbehave help you to implement this pattern by providing WebDriverPage. So your parent class of each page might look like,
public abstract class AbstractPage extends WebDriverPage {
    public AbstractPage(WebDriverProvider driverProvider) {
        super(driverProvider);
    }
    ...
}
And a simple page can be like the following.
/*
 * Contains page specific
 *   HTML structure
 *   Assertions
 *   Navigation
 */
public class ResumePage extends AbstractPage {

    public ResumePage(WebDriverProvider driverProvider) {
        super(driverProvider);
    }
    
    public void open(){
        findElement(By.linkText("Resume")).click();
    }
    
    public void assertShown() {
        String output = findElement(By.cssSelector("#menu .selected a")).getText();
        assertTrue("Resume menu should be selected", output.contains("Resume"));
    }

}

Use annotation

Use jbehave annotation to simplify the code.

public class LoginPage extends AbstractPage {
 @FindBy(how = How.NAME, using = "username")
 private WebElement userNameElem;

 @FindBy(how = How.NAME, using = "password")
 private WebElement passwordElem;

 @FindBy(how = How.NAME, using = "role")
 private WebElement roleElem;

 @FindBy(how = How.NAME, using = "B3")
 private WebElement submitButton;

 public void loginAs(String profileName) {
  User user = User.getUser(profileName);
  userNameElem.sendKeys(user.getUserName());
  passwordElem.sendKeys(user.getPassword());
  roleElem.sendKeys(user.getRole());
  submitButton.submit();
 }

}

Sample project

The sample project can be found here.

References



1 comment:

Oleg said...

WebDriver, not the Webdrive