Selenium Page Objects
Selenium User's Group 04/19/2012
Paul Grandjean
What's Your Background?
Are you a QA person? Developer?
Have you used Selenium?
Are you thinking about using Selenium?
Have you used WebDriver? RC? IDE? Grid?
Have you used other automation tools?
My Background
Using Selenium since 2008.
Used IDE, RC, WebDriver, and now Grid.
Using Page Objects on my third project now.
What problems have you had with automation?
Common Automation Difficulties
False Failures (sometimes called 'brittleness' or a 'brittle test suite').
Maintaining the suite under constant UI changes.
Are you making the same changes to tests in multiple places?
In it's extreme, groups will scrap an automation suite.
What are Page Objects?
A (test) Design Pattern.
Design patterns solve common coding problems that many have encountered.
Reduces false failures by aiding maintainability.
Page Objects ...
Provide a single place in your code to update 'locaters' when the UI changes.
Correspond to a webpage in your AUT.
Or a part of a page making it a 'page component'.
Provide methods for all services that UI page provides to the end-user.
Page Object methods...
Type in text input fields.
Click buttons.
Select options from drop-downs.
Read text labels.
Read data as text.
Select radio buttons.
Page Object Example
private WebElement logo;
private WebElement userNameDisplay;
private WebElement logoutButton;
public Header(WebDriver driver) {
this.driver = driver;
}
public boolean isLogoDisplayed() {
logo = driver.findElement(By.id(“logo-id”));
return logo.isDisplayed();
}
public String getUserName() {
userNameDisplay =
driver.findElement(By.className(“userNameClassName”));
return userNameDisplay.getText();
}
public void clickLogoutButton() {
logoutButton = driver.findElement(By.cssSelector(“div > div > a”));
logoutButton.click();
}
}
So what does the test do?
Only interacts with the UI by calling page object methods.
Never interacts directly with a WebElement (WebDriver) or a Sel-RC method.
The automation technology is always encapsulated by the page objects, isolating support for UI changes in one place.
Test Example
@Test
void HeaderTest {
// ... login to AUT ...
Header header = new Header(driver);
AssertTrue(header.isLogoDisplayed(), "Logo isn't displayed.");
AssertEquals(header.getUserName(),
"MyUserName", "Expected username not found.");
header.clickLogoutButton();
// ... use another page object to validate
what the user sees after a logout.
}
Test Example
HomePage homePage = header.clickLogoutButton();
// ... use another page object to validate
what the user sees after a logout.
AssertTrue(HomePage homePage.isDisplayed(),
“Home page not displayed after logout”);
A page object can return another page object after a click.
How to Write a Page Object
In WebDriver + Java...
Import WebElement, WebDriver.
Create a class.
Be sure it has a private WebDriver, this can be in a base class.
Create private WebElements for each UI element the page object needs to control.
How to Write A Page Object (cont)
In WebDriver + Java...
Write methods for each WebElement that perform the needed operations—click()? GetText(), etc. And encapsulate the actual WebElement operations.
What is a Page Factory?
A Page Factory is used to initialize page objects.
Initialization ....
Passes the WebDriver instance to the page object.
Automatically generates findElement() calls.
Provided via org.openqa.selenium.ui.support
Using the Page Factory
@Test
void HeaderTest {
Header header = PageFactory.initElements(driver, Header.class);
AssertTrue(header.isLogoDisplayed(), "Logo isn't displayed.");
@FindBy Annotations
The PageFactory will generate your findElement calls if you use the @FindBy annotation.
This is why you use the PageFactory rather then a constructor.
@FindBy Annotations public class Header {
private WebDriver driver;
@FindBy(id=”logo-id”)
private WebElement logo;
@FindBy(class=”userNameClassName”)
private WebElement userNameDisplay;
@FindBy(css=”div > div > a”)
private WebElement logoutButton;
@FindBy Annotations
public Header(WebDriver driver) {
this.driver = driver;
}
// findElement() calls no longer needed.
public boolean isLogoDisplayed() {
return logo.isDisplayed();
}
public String getUserName() {
return userNameDisplay.getText();
}
public void clickLogoutButton() {
logoutButton.click();
}
}
Synchronization Issues
A page object can (should?) be responsible for it's own 'readiness'.
It can block until all needed UI elements have been rendered.
You could do this in the test, but the convention is to do this in the page object.
since no further testing is relevant unless the page object isReady();
Page Object isReady() Example @FindBy(id=”logo-id”)
private WebElement logo;
@FindBy(class=”userNameClassName”)
private WebElement userNameDisplay;
@FindBy(css=”div > div > a”)
private WebElement logoutButton;
public Header(WebDriver driver) {
this.driver = driver;
}
public boolean isLogoDisplayed() {
logo = Waiter.waitFor(driver, By.id(“logo-id”), 60);
// use default timeout
userNameDisplay.waitFor(driver, By.class(“userNameClassName”));
logoutButton = Waiter.waitFor(driver, By.cssSelector(“div > div > a”));
}
Other Handy Techniques
Massive use of utility functions.
Behavior-based 'go-to-here' test setup utilities.
Database helper utilities.
**** WaitFor (element) utility for handling timing issues. ****
Test automation is Asynchronous with a CAPITAL 'A'!!!!
Other Handy Techniques
Handling lists and tables of data.
Disadvantages
In the beginning, it can take longer before the first few tests are built and ready to run.
May need to explain this to your developers and managers.
Cannot generate code via Sel-IDE.
Resources Selenium Wiki
http://code.google.com/p/selenium
/wiki/PageFactory
Search on “Page Objects” and “Page Factory”
Many have blogged on this topic.