Framework · Java
Page objects and design patterns
The Page Object Model, page factories and patterns that keep tests tidy.
Learn: Page objects and design patterns in a Selenium Java framework9 minute read
The Page Object Model (POM) is a way to organize UI test code: every page of the app, or every reusable part of a page, gets its own class. "What is the Page Object Model?" is one of the most common framework questions. It is usually followed by "and Page Factory?" and "which design patterns does your framework use?". This page answers all three for a Selenium Java framework, starting from the problem POM solves.
The problem
A locator is the instruction that tells Selenium how to find an element on the page, for example By.id("username") means "the element whose id is username". Imagine fifty tests that each contain driver.findElement(By.id("username")). One day a developer renames the field to user-name. Now fifty tests fail, and you edit fifty files. With page objects, that locator lives in one class, so you change one line.
A page object
The class keeps the page's locators private and offers public methods for what a user can do there. Tests call those methods and hold the assertions (the checks that fail a test). extends BasePage means the class inherits the methods of the base page shown next.
public class LoginPage extends BasePage {
private final By user = By.id("username");
private final By pass = By.id("password");
private final By submit = By.cssSelector("button[type='submit']");
private final By error = By.cssSelector(".login-error");
public LoginPage(WebDriver driver) { super(driver); }
public DashboardPage loginAs(String name, String password) {
type(user, name);
type(pass, password);
click(submit);
return new DashboardPage(driver);
}
public String errorText() {
return visible(error).getText();
}
}
The waits live in a base page that every page extends. A wait tells Selenium to pause until an element is ready, so no test ever needs a fixed sleep:
public abstract class BasePage {
protected final WebDriver driver;
protected final WebDriverWait wait;
protected BasePage(WebDriver driver) {
this.driver = driver;
this.wait = new WebDriverWait(driver, Duration.ofSeconds(10));
}
protected WebElement visible(By by) {
return wait.until(ExpectedConditions.visibilityOfElementLocated(by));
}
protected void click(By by) {
wait.until(ExpectedConditions.elementToBeClickable(by)).click();
}
protected void type(By by, String text) {
WebElement el = visible(by);
el.clear();
el.sendKeys(text);
}
}
A test then reads like a user story: new LoginPage(driver).loginAs("asha", "secret"), followed by an assertion on the dashboard.
Rules that keep page objects healthy
- No assertions in page objects. Return data and let the test decide, so the same page serves positive and negative tests.
- Return the next page.
loginAsreturns aDashboardPage, so a flow readsloginPage.loginAs(...).openOrders().cancel(id). - Keep
Bylocators and find elements inside each method. A stored element can go stale (the page was redrawn, so the stored element no longer points to anything on the screen); a locator cannot. - Give widgets used on many pages, such as a header or a data table, their own component class.
- Let the base test set up state, such as logging in through the API, and hand the test a ready page. Page objects then stay thin and never manage the driver.
Page Factory
Selenium Java's support library offers Page Factory, an annotation style for page objects:
@FindBy(id = "username") private WebElement user;
@FindBy(css = "button[type='submit']") private WebElement submit;
public LoginPage(WebDriver driver) {
PageFactory.initElements(driver, this);
}
initElements puts a proxy in each annotated field. A proxy is a stand-in object that finds the real element only when you use it. Without initElements the fields stay null. @FindBys chains several locators, @FindAll collects the matches of any of them, @CacheLookup keeps the first element found and never looks again (risky on pages that re-render), and AjaxElementLocatorFactory adds a timeout to each lookup.
So POM is the design pattern, and Page Factory is one way to build it in Java. Many teams prefer plain By fields, because the usual explicit waits take a By and nothing happens behind your back.
Object repository
An object repository is one central place for locators. In a code-based framework the page classes are that place. Some teams keep locators in a properties or JSON file instead, which people who do not code can edit, but the compiler can no longer check them.
Design patterns you can point to
- Factory (one place that creates objects for you): a
DriverFactoryreturns Chrome, Firefox or aRemoteWebDriver(a browser running on another machine) from configuration. - Singleton (only one object of a class exists): one configuration reader per run. The driver is not a plain singleton, because parallel tests (tests running at the same time, see Parallel and CI runs) need one driver per thread. A
ThreadLocalis a Java holder that gives each thread its own copy. - Builder (build an object step by step with defaults): test data such as
new UserBuilder().withRole("admin").build(). - Fluent interface (calls that chain one after another): page methods that return the next page.
Framework types
A data-driven framework runs the same steps with external data. A keyword-driven one writes steps as keywords in tables. A BDD framework writes scenarios in Gherkin, a plain-English format (see BDD with Cucumber or behave). A hybrid mixes them, usually page objects plus data-driven tests, and that is what most real frameworks are.
How to answer in an interview
Define POM in one sentence, say why it helps (one place to fix a locator, readable tests), and show a page method that returns the next page. If Page Factory comes up, say it is a Java helper for building page objects, explain initElements, and say which style your team uses and why.
Common mistakes
- Putting assertions inside page methods.
- Calling Page Factory a different pattern from POM.
- Making the driver a static singleton, which breaks parallel runs.
- Using
@CacheLookupon dynamic pages and getting stale element errors.
Recap
A page object keeps one page's locators and user actions in one class, and tests keep the assertions. Waits go in a base page, and methods return the next page. Page Factory is an optional Java way to fill element fields. Factory, builder and fluent methods are the patterns most frameworks really use.
9 questions0/9 solved
| Status | Question | Difficulty | Asked in |
|---|---|---|---|
| Not started | In the Page Object Model, where do assertions belong? | Easy-Medium | Asked in: Accenture, Infosys, TCS +1 |
| Not started | What is the Page Object Model and why use it? (Java) | Easy-Medium | Asked in: Accenture, Infosys, TCS +1 |
| Pro+ | Page Object Model versus Page Factory | Medium | Asked in: Infosys, TCS, Wipro |
| Pro+ | What does PageFactory.initElements do? | Medium | Asked in: Infosys, TCS, Wipro |
| Pro+ | What should loginAs() return in a page object? | Medium | Asked in: Infosys |
| Pro+ | Design patterns in your framework (Java) | Medium-Hard | Asked in: Infosys |
| Pro+ | Framework types: data-driven, keyword-driven, hybrid and BDD | Medium | Asked in: Accenture, Capgemini, Infosys +1 |
| Pro+ | What is an object repository? | Easy | Asked in: Accenture, TCS |
| Pro+ | Should you still use page objects, or fixtures and helpers instead? | Medium-Hard |
The first 2 questions of each topic are free. Pro+ opens every question.