The Page Object Model (POM) in Selenium is a structured design pattern that organizes web page elements and actions into separate classes. It improves the clarity and maintainability of test scripts. POM encapsulates page elements and interactions within dedicated objects. This minimizes code duplication and simplifies updates when the user interface changes.
This approach helps teams manage automation scripts more efficiently. It supports reusability across different tests and reduces effort when pages change. Tests become easier to read and maintain, which speeds up development cycles. POM also improves consistency across the automation suite, making large-scale testing more reliable and structured.
This article explains how the Page Object Model works in Selenium, its implementation, and best practices for building scalable test automation frameworks.
The Page Object Model (POM) in Selenium is a design pattern that represents each web page of an application as a separate class. Each class contains the web elements of that page and the actions or methods that can be performed on them. This abstraction separates the test logic from the page structure, making test scripts cleaner and easier to maintain.
For example, a login page can have a LoginPage class with fields for username, password, and a login button, along with a method to perform the login action. Tests then interact with this class instead of directly accessing page elements, reducing duplication and improving readability.
The Page Object Model enhances the structure and maintainability of automation scripts. It allows teams to manage UI changes efficiently while keeping tests organized.
Implementing the Page Object Model involves creating separate classes for each page of the application. Each class contains the locators for web elements and methods that define actions performed on the page. Tests interact with these classes instead of directly accessing the UI, which keeps test scripts clean and focused on validation rather than implementation details.
Consider a web application with a login page. A tester wants to automate logging in with valid credentials. Instead of writing locators and actions directly in the test script, a LoginPage class can encapsulate all elements and actions related to login.
This class stores the locators for the username, password, and login button, and provides methods to interact with them. A combined login method allows performing the full login in a single call, keeping tests clean and maintainable.
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.By;
public class LoginPage {
WebDriver driver;
public LoginPage(WebDriver driver) {
this.driver = driver;
}
// Locators for username, password, and login button
By usernameField = By.id("username");
By passwordField = By.id("password");
By loginButton = By.id("loginBtn");
// Methods to interact with page elements
public void enterUsername(String username) {
driver.findElement(usernameField).sendKeys(username); // Enter username
}
public void enterPassword(String password) {
driver.findElement(passwordField).sendKeys(password); // Enter password
}
public void clickLogin() {
driver.findElement(loginButton).click(); // Click login button
}
// Combined action to perform login
public void login(String username, String password) {
enterUsername(username);
enterPassword(password);
clickLogin();
}
}
Once the page class is defined, the test script can use it to perform login. This separates the test logic from the page structure, making tests easier to read, maintain, and reuse.
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.*;
public class LoginTest {
WebDriver driver;
@BeforeMethod
public void setup() {
driver = new ChromeDriver(); // Open Chrome browser
driver.get("https://example.com/login"); // Navigate to login page
}
@Test
public void validLoginTest() {
LoginPage loginPage = new LoginPage(driver); // Create page object
loginPage.login("testuser", "password123"); // Perform login
// Add assertions here to verify successful login
}
@AfterMethod
public void teardown() {
driver.quit(); // Close browser after test
}
}
Page Factory is a Selenium feature that simplifies the implementation of the Page Object Model by handling the initialization of web elements automatically.
Instead of manually locating elements with driver.findElement, Page Factory uses annotations like @FindBy to define elements, which are then initialized when the page class is instantiated. This reduces boilerplate code and makes page classes cleaner and easier to manage.
For example, the LoginPage can be rewritten using Page Factory so that all elements are automatically initialized, but the test script still interacts with the page through the same methods. This maintains the separation of test logic and page structure while improving readability and maintainability.
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
public class LoginPage {
WebDriver driver;
public LoginPage(WebDriver driver) {
this.driver = driver;
PageFactory.initElements(driver, this); // Initialize elements using Page Factory
}
@FindBy(id = "username")
WebElement usernameField; // Username input field
@FindBy(id = "password")
WebElement passwordField; // Password input field
@FindBy(id = "loginBtn")
WebElement loginButton; // Login button
// Methods to interact with page elements
public void enterUsername(String username) {
usernameField.sendKeys(username);
}
public void enterPassword(String password) {
passwordField.sendKeys(password);
}
public void clickLogin() {
loginButton.click();
}
public void login(String username, String password) {
enterUsername(username);
enterPassword(password);
clickLogin();
}
}
In this version, Page Factory automatically handles element initialization, reducing repetitive findElement calls. The test script using this class remains the same, keeping the workflow modular and consistent.
Consider a web application with a login page. A tester wants to automate logging in with valid credentials. Using Page Factory simplifies element initialization while keeping the Page Object Model structure intact. All elements are defined with annotations and automatically initialized when the page class is instantiated, so the test script can focus purely on workflow and validation.
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
public class LoginPage {
WebDriver driver;
public LoginPage(WebDriver driver) {
this.driver = driver;
PageFactory.initElements(driver, this); // Initialize elements using Page Factory
}
@FindBy(id = "username")
WebElement usernameField; // Username input field
@FindBy(id = "password")
WebElement passwordField; // Password input field
@FindBy(id = "loginBtn")
WebElement loginButton; // Login button
// Methods to interact with page elements
public void enterUsername(String username) {
usernameField.sendKeys(username);
}
public void enterPassword(String password) {
passwordField.sendKeys(password);
}
public void clickLogin() {
loginButton.click();
}
public void login(String username, String password) {
enterUsername(username);
enterPassword(password);
clickLogin();
}
}
The test script uses the page object in the same way as standard POM, maintaining separation of test logic and page structure while benefiting from cleaner code.
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.*;
public class LoginTest {
WebDriver driver;
@BeforeMethod
public void setup() {
driver = new ChromeDriver(); // Open Chrome browser
driver.get("https://example.com/login"); // Navigate to login page
}
@Test
public void validLoginTest() {
LoginPage loginPage = new LoginPage(driver); // Create page object with Page Factory
loginPage.login("testuser", "password123"); // Perform login
// Add assertions to verify successful login
}
@AfterMethod
public void teardown() {
driver.quit(); // Close browser after test
}
}
Using Page Factory, all elements are automatically initialized, reducing boilerplate code and making page classes cleaner. The test script remains readable, maintainable, and fully aligned with Page Object Model principles, making it easier to scale automation across larger applications.
The Page Object Model (POM) and Page Factory both organize test automation by separating page structure from test logic, but they differ in how elements are defined and initialized.
POM requires manual initialization of elements using driver.findElement, giving full control over locating elements but requiring more boilerplate code. Page Factory uses annotations like @FindBy to automatically initialize elements when the page class is instantiated, reducing repetitive code and improving readability.
Example Scenario:
In a login page, POM requires calling driver.findElement for each field and button. Using Page Factory, these elements are annotated and automatically initialized, so the test can simply call loginPage.login(username, password) without managing element initialization manually.
Both approaches maintain modularity and separation of test logic from page structure. Choosing between them depends on project size, team preference, and the need for cleaner code versus full control over element handling.
Building page objects correctly is crucial to ensure that automation scripts remain maintainable, readable, and scalable. Following best practices helps teams avoid common pitfalls such as code duplication, brittle tests, and difficulty in updating locators when the UI changes.
The Page Object Model and Page Factory provide a structured approach to building maintainable and scalable Selenium test automation frameworks. By encapsulating page elements and actions within dedicated classes, teams can reduce code duplication, simplify maintenance, and improve readability across large test suites.
For teams looking to scale Selenium testing across multiple browsers and devices, BrowserStack Automate offers a cloud-based platform to run these tests efficiently. It allows executing scripts on real browsers and devices, provides parallel execution for faster results, and integrates seamlessly with existing frameworks.
Run Selenium Tests on Cloud
Get visual proof, steps to reproduce and technical logs with one click
Continue reading
Try Bird on your next bug - you’ll love it
“Game changer”
Julie, Head of QA
Try Bird later, from your desktop