Page Object pattern with selenium WebDriver

End to end or acceptance testing is most dificult part of testing lifecycle. In most cases it involves some GUI testing. People tend to treat test code as some less importatnt part of system and pay more attention to quality an readability when the test code main priority is that should be easy to understand and clear.

Here i will shortly describe Page Object patter that keeps our GUI testing code clean and easy to maintain.

Source code to this article available at github:
https://github.com/michal-lipski/page-objects-webdriver

Page Object pattern

A Page Object models UI page as object within the test code. This reduces the amount of duplicated code and centralizes web page data.It helps a lot with refactoring when UI changes which tends to happen very often.

Main concepts of Page Objects:

  • the public methods of Page Object should  represent the services that the page offers
  • hide all the internal details of the page (locators,elements ids,Urls) in Page Object
  • expose only services that page offers as a public methods
  • don’t make assertions in Page Object
  • sevice methods should return other Page Objects to reflect application flow
  • make sure that corresponding web page is fully loaded when you return Page Object
  • model different services for different results of one action

Page Object representing login page with login functionality covered:

public class GitHubLoginPage extends GitHubPage {

    @Override
    public String getUrl() {
        return "/login";
    }
 
    public void login(String login, String password) {
	getDriver().findElement(By.id("login_field")).sendKeys(login);        
	getDriver().findElement(By.name("password")).sendKeys(login);   
        getDriver().findElement(By.name("commit")).click();
    }
}

We can see that the login service method code looks complicated because of direct use of string literals to find elements on page. If we use those elements in other methods probably this would lead to introducing some constants. But there is a better way to do this with Selenium PageFactory.

Selenium WebDriver and PageFactory

PageFactory helps to remove some boiler-plate code from your Page Objects. Instead of declaring String describing element location we use WebElements.
So instead of:

driver.findElement(By.id("login_field")).sendKeys(login);

we have:

loginField.sendKeys(login);

To connect web page element with Page Object element we use @FindBy annotation:

@FindBy(id="login_field")
WebElement loginField;

@FindBy(name="password")
WebElement passwordField;

@FindBy(className="error_box")
WebElement errorBox;

Using findBy annotation we can describe how selenium should search for our element on page. We can search by id, name, className, css, xpath expression and others.
Before we can use those elements we have to inintialize our Page Object class.

GitHubLoginPage page = PageFactory.initElements(driver, GitHubLoginPage.class);

Putting it all together

Page Object give us ability to build fluent calls reflecting user interaction with application. Its clear to read and reduces complexity of test method.

Here is final version of a Page Object representing github login page.
It extends base page which have common code for initialization of PageFactory and implementation of wait until page is loaded. Wait condition is based on getPageLoadCondition() implementation delivered by subclasses.

public class GitHubLoginPage extends GitHubPage {

	@FindBy(id = "login_field")
	WebElement loginField;

	@FindBy(name = "password")
	WebElement passwordField;

	@FindBy(name = "commit")
	WebElement commitButton;

	@FindBy(className = "error_box")
	WebElement errorBox;

        @Override
	protected ExpectedCondition getPageLoadCondition() {
		//condition for full page load 
                return ExpectedConditions.visibilityOf(loginField);
	}

	@Override
	public String getUrl() {
		return "/login";
	}

	public void login(String login, String password) {
		loginField.sendKeys(login);
		passwordField.sendKeys(password);
		commitButton.click();
	}

	public boolean isLoginError() {
		return errorBox.isDisplayed();
	}

	public String getErrorMessage() {
		return errorBox.getText();
	}
}

And test method:

@Test
public void should_not_login_with_wrong_credentials() {
	//given
	GitHubLoginPage loginPage = new GitHubHomePage().open().goToLoginPage();
	//when
	loginPage.login("user", "password");
	//then
	assertThat(loginPage.isLoginError()).isTrue();
}
Advertisements

4 responses to “Page Object pattern with selenium WebDriver

  1. Pingback: khemlall.info » Page Object pattern with selenium WebDriver

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