Page Object Model
Page Object Model Interview with follow-up questions
1. What is the Page Object Model in Selenium?
The Page Object Model (POM) is a design pattern where each web page—or significant component of a page—is represented by a dedicated Java (or Python/C#) class. The class encapsulates the page's element locators and exposes methods that represent the actions a user can perform on that page. Tests interact with page objects rather than making raw WebDriver calls directly.
Core idea
// Page class
public class LoginPage {
private final WebDriver driver;
private final By usernameField = By.id("username");
private final By passwordField = By.id("password");
private final By loginButton = By.cssSelector("button[type='submit']");
public LoginPage(WebDriver driver) {
this.driver = driver;
}
public DashboardPage login(String username, String password) {
driver.findElement(usernameField).sendKeys(username);
driver.findElement(passwordField).sendKeys(password);
driver.findElement(loginButton).click();
return new DashboardPage(driver);
}
}
// Test
LoginPage loginPage = new LoginPage(driver);
DashboardPage dashboard = loginPage.login("[email protected]", "secret");
Why interviewers value POM
- Maintainability: When a locator changes, you update it in one class, not in every test that uses that element.
- Readability: Tests read as business actions (
loginPage.login(...)) rather than WebDriver mechanics (driver.findElement(By.id("username")).sendKeys(...)). - Reusability: The same page object is used by multiple tests.
- Separation of concerns: Page structure and navigation logic live in page classes; test assertions and test data live in test classes.
PageFactory vs. plain By locators
PageFactory.initElements(driver, this) with @FindBy annotations is a convenience that initializes WebElement fields lazily. Many modern teams prefer explicit By fields (as shown above) because they are easier to debug, do not require PageFactory initialization, and work cleanly with explicit waits (WebDriverWait).
Follow-up 1
Why is it important?
The Page Object Model is important because it promotes reusability, maintainability, and readability of the test code. By creating separate classes for each page or component, it becomes easier to make changes to the application's UI without impacting the test code. It also allows for better collaboration between testers and developers, as they can work on their respective parts independently.
Follow-up 2
Can you explain how it improves the readability of the code?
The Page Object Model improves the readability of the code by providing a clear and structured way to interact with web elements. Each page or component is represented by a separate class, which contains methods to interact with the elements on that page. This makes the code more readable and understandable, as the test steps are written in a more natural and descriptive manner.
Follow-up 3
How does it help in maintaining the code?
The Page Object Model helps in maintaining the code by providing a centralized and reusable repository of web elements and their associated actions. If there are any changes in the UI, such as the locators or the structure of the page, the changes can be made in the corresponding page class, without affecting the test code. This reduces the effort required to update the tests and ensures that the tests remain stable even when the application undergoes changes.
Follow-up 4
What are the advantages of using Page Object Model?
Some advantages of using the Page Object Model are:
- Reusability: The page classes can be reused across multiple tests, reducing code duplication.
- Maintainability: Changes to the UI can be easily managed in the page classes, without impacting the test code.
- Readability: The code becomes more readable and understandable, as the test steps are written in a descriptive manner.
- Collaboration: Testers and developers can work on their respective parts independently, as the page classes provide a clear separation of concerns.
- Scalability: The Page Object Model allows for easy scaling of the test suite, as new page classes can be added to accommodate new features or pages in the application.
2. How do you implement the Page Object Model in Selenium?
Implementing the Page Object Model involves creating one class per page (or per significant component), keeping locators and WebDriver interactions inside those classes, and having tests call the page object methods.
Step 1: Create a base page (optional but common)
public class BasePage {
protected WebDriver driver;
protected WebDriverWait wait;
public BasePage(WebDriver driver) {
this.driver = driver;
this.wait = new WebDriverWait(driver, Duration.ofSeconds(10));
}
}
Step 2: Create a page class
public class LoginPage extends BasePage {
private final By usernameField = By.id("username");
private final By passwordField = By.id("password");
private final By submitButton = By.cssSelector("button[type='submit']");
public LoginPage(WebDriver driver) { super(driver); }
public DashboardPage login(String user, String pass) {
wait.until(ExpectedConditions.visibilityOfElementLocated(usernameField))
.sendKeys(user);
driver.findElement(passwordField).sendKeys(pass);
driver.findElement(submitButton).click();
return new DashboardPage(driver);
}
}
Step 3: Use page objects in tests
@Test
public void loginWithValidCredentials() {
driver.get("https://example.com/login");
DashboardPage dashboard = new LoginPage(driver).login("[email protected]", "secret");
assertTrue(dashboard.isWelcomeMessageDisplayed());
}
PageFactory alternative
PageFactory.initElements(driver, this) with @FindBy annotations initializes WebElement fields lazily. It is concise but has drawbacks: lazy-proxy WebElements can throw StaleElementReferenceException when pages re-render, and they work less naturally with WebDriverWait. Many modern teams prefer explicit By locators for clarity and easier debugging.
Fluent page chaining
Returning the next page object from action methods (return new DashboardPage(driver)) creates a readable chain and catches navigation mistakes at compile time.
Follow-up 1
What are the steps involved in implementing it?
The steps involved in implementing the Page Object Model (POM) in Selenium are as follows:
- Identify the web pages and their elements that need to be automated.
- Create a separate class for each web page, representing the page as an object.
- Define the web elements of the page as instance variables in the class.
- Implement methods in the class to perform actions on the web elements.
- Use these page classes in your test code to interact with the web pages.
These steps help in creating a structured and maintainable test automation framework.
Follow-up 2
Can you provide a sample code snippet?
Sure! Here is a sample code snippet that demonstrates the implementation of the Page Object Model (POM) in Selenium using Java:
public class LoginPage {
private WebDriver driver;
private WebElement usernameInput;
private WebElement passwordInput;
private WebElement loginButton;
public LoginPage(WebDriver driver) {
this.driver = driver;
PageFactory.initElements(driver, this);
}
public void enterUsername(String username) {
usernameInput.sendKeys(username);
}
public void enterPassword(String password) {
passwordInput.sendKeys(password);
}
public void clickLoginButton() {
loginButton.click();
}
}
In this example, the LoginPage class represents the login page of a web application. The web elements of the page (usernameInput, passwordInput, loginButton) are defined as instance variables. The methods (enterUsername, enterPassword, clickLoginButton) perform actions on these web elements. The PageFactory.initElements method is used to initialize the web elements with the driver.
Follow-up 3
What are the best practices while implementing Page Object Model?
While implementing the Page Object Model (POM) in Selenium, it is important to follow some best practices to ensure the effectiveness and maintainability of the framework. Here are some best practices:
- Create a separate class for each web page: This helps in organizing the code and makes it easier to maintain.
- Use meaningful names for classes and methods: This improves the readability of the code and makes it easier to understand the purpose of each method.
- Keep the page classes independent: Avoid creating dependencies between page classes to make them reusable and independent.
- Use PageFactory for initializing web elements: PageFactory is a utility class in Selenium that helps in initializing the web elements with the driver.
- Use inheritance for common functionality: If multiple pages have common functionality, create a base page class and inherit from it.
By following these best practices, you can create a robust and maintainable test automation framework using the Page Object Model.
3. What is the role of Page Factory in Page Object Model?
PageFactory is a support class in Selenium that initializes page object fields annotated with @FindBy. Calling PageFactory.initElements(driver, this) in the page class constructor sets up WebElement proxies that locate the actual element on the page each time the field is accessed.
How it works
public class LoginPage {
@FindBy(id = "username")
private WebElement usernameField;
@FindBy(css = "button[type='submit']")
private WebElement submitButton;
public LoginPage(WebDriver driver) {
PageFactory.initElements(driver, this);
}
public void enterUsername(String username) {
usernameField.sendKeys(username);
}
}
usernameField is not actually located at construction time. Each call to usernameField.sendKeys(...) triggers a fresh driver.findElement() lookup behind the scenes. This is called lazy initialization.
When PageFactory is useful
- Reduces boilerplate: no explicit
Bydeclarations needed. - Supports
@FindBys(AND logic) and@FindAll(OR logic) for complex locators. - Familiar to teams coming from frameworks that use annotation-driven configuration.
Limitations and why many teams skip it
- The lazy proxy approach makes it harder to use
WebDriverWait:wait.until(ExpectedConditions.visibilityOf(usernameField))works, but if the element doesn't exist yet the proxy still returns an object, and errors surface later than expected. - Stale element handling is less transparent—the proxy re-finds the element on each access, but in some SPA scenarios this still results in
StaleElementReferenceException. @FindByannotations are less searchable than namedByconstants when refactoring locators.
In 2026, PageFactory remains valid and works correctly. The choice between PageFactory and plain By fields comes down to team preference and how heavily the project relies on explicit waits.
Follow-up 1
How does it work?
Page Factory works by using the concept of lazy initialization. When we create an instance of a page class using Page Factory, it initializes the elements of the web page only when they are accessed for the first time. This helps in improving the performance of the test scripts as it avoids unnecessary initialization of elements that are not used. Page Factory uses the @FindBy annotation to locate and initialize the elements of a web page.
Follow-up 2
Can you provide an example where you used Page Factory?
Sure! Here is an example where I used Page Factory in a Selenium test script:
public class LoginPage {
@FindBy(id = "username")
private WebElement usernameInput;
@FindBy(id = "password")
private WebElement passwordInput;
@FindBy(id = "loginButton")
private WebElement loginButton;
public LoginPage(WebDriver driver) {
PageFactory.initElements(driver, this);
}
public void login(String username, String password) {
usernameInput.sendKeys(username);
passwordInput.sendKeys(password);
loginButton.click();
}
}
In this example, the LoginPage class represents the web page with username input, password input, and login button. The elements are initialized using the @FindBy annotation, and the PageFactory.initElements(driver, this) method is used to initialize the elements in the constructor of the page class. The login method is used to enter the username and password and click on the login button.
Follow-up 3
What are the advantages of using Page Factory?
There are several advantages of using Page Factory in Page Object Model:
Code reusability: Page Factory allows us to define the elements of a web page once and reuse them in multiple test scripts. This helps in reducing code duplication and improving maintainability.
Easy maintenance: By using Page Factory, we can easily update the locators of elements in a single place, i.e., the page class. This makes it easier to maintain the test scripts when there are changes in the web application.
Improved performance: Page Factory uses lazy initialization, which means that the elements of a web page are initialized only when they are accessed for the first time. This helps in improving the performance of the test scripts by avoiding unnecessary initialization of elements.
Enhanced readability: By using Page Factory, the code becomes more readable as the elements of a web page are represented as variables with meaningful names. This makes it easier to understand and maintain the test scripts.
4. How do you handle dynamic elements in Page Object Model?
Dynamic elements—elements whose attributes, locators, or presence change at runtime—require extra care in the Page Object Model. The goal is to keep the page object flexible without exposing implementation details to tests.
Use methods with parameters for dynamic locators
When an element's locator depends on runtime data (e.g., a row in a table identified by a username), build the locator inside a method:
public WebElement getRowByUsername(String username) {
By rowLocator = By.xpath("//tr[td[text()='" + username + "']]");
return wait.until(ExpectedConditions.visibilityOfElementLocated(rowLocator));
}
Use robust XPath functions
contains(), starts-with(), and normalize-space() make locators tolerant of partial or whitespace-padded text:
private final By dynamicButton = By.xpath("//button[contains(@id, 'submit')]");
Apply explicit waits inside page object methods
Never rely on implicit waits or Thread.sleep(). Apply WebDriverWait inside the page object so tests remain clean:
public String getNotificationText() {
return wait.until(ExpectedConditions.visibilityOfElementLocated(
By.cssSelector(".notification"))).getText();
}
Avoid caching element references
Do not store a WebElement in a field if the element can disappear and re-appear (common in SPAs). Re-locate the element each time it is needed, or use By locators rather than pre-fetched WebElement references.
Return boolean presence checks
For elements that may or may not be present, provide a helper that returns a boolean:
public boolean isErrorMessageDisplayed() {
return !driver.findElements(By.id("errorMsg")).isEmpty();
}
This pattern keeps assertions simple in tests while hiding the timing logic inside the page object.
Follow-up 1
Can you provide a sample code snippet?
Sure! Here's a sample code snippet that demonstrates how to handle dynamic elements in Page Object Model:
from selenium.webdriver.common.by import By
class HomePage:
def __init__(self, driver):
self.driver = driver
self.dynamic_element_id = self.generate_dynamic_element_id()
self.dynamic_element_locator = (By.ID, self.dynamic_element_id)
def generate_dynamic_element_id(self):
# Code to generate the dynamic element ID
return dynamic_element_id
Follow-up 2
What are the challenges faced while handling dynamic elements and how do you overcome them?
Some of the challenges faced while handling dynamic elements in Page Object Model include:
- Identifying the pattern or logic behind the dynamic changes in the element's attributes or positions.
- Ensuring that the dynamic element is loaded and available before interacting with it.
To overcome these challenges, we can:
- Analyze the HTML structure and behavior of the dynamic element to identify any patterns or logic that can be used to generate the dynamic locator.
- Use explicit waits to wait for the dynamic element to be visible or clickable before interacting with it.
Here's an example of how to use explicit waits to handle dynamic elements:
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class HomePage:
def __init__(self, driver):
self.driver = driver
self.dynamic_element_locator = (By.ID, 'dynamic_element_id')
def click_dynamic_element(self):
wait = WebDriverWait(self.driver, 10)
element = wait.until(EC.element_to_be_clickable(self.dynamic_element_locator))
element.click()
5. Can you explain the concept of encapsulation in Page Object Model?
Encapsulation in the Page Object Model means hiding the internal implementation details of a page—its locators, WebDriver calls, and navigation logic—inside the page class, and exposing only a clean, intention-revealing public API to tests.
What is hidden (private)
- Element locators (
Byfields or@FindByannotations) - Raw WebDriver calls (
driver.findElement(...),driver.get(...)) - Low-level interaction code (
sendKeys,click,getText) - Explicit wait conditions
What is exposed (public)
- Business-level action methods:
login(user, pass),submitOrder(),searchForProduct(name) - State-query methods:
isErrorMessageDisplayed(),getWelcomeText() - Navigation methods that return the next page object:
return new DashboardPage(driver)
Why this matters
// Without encapsulation — test knows too much about the page
driver.findElement(By.id("username")).sendKeys("[email protected]");
driver.findElement(By.cssSelector("button[type='submit']")).click();
// With encapsulation — test expresses intent
loginPage.login("[email protected]", "secret");
If the submit button's CSS selector changes, the first approach requires updating every test that clicks it. With POM encapsulation, only LoginPage needs updating.
Locator change impact
Encapsulation localizes the impact of UI changes. A relocated field, a renamed id, or a restructured form requires a one-line change in the page class—all tests that use loginPage.login(...) continue to work without modification.
Tests as business documentation
When tests consist entirely of page object method calls, they read like user stories. A non-technical stakeholder can understand loginPage.login(...); dashboard.navigateToOrders(); orderPage.placeOrder(item) without knowing anything about XPath or WebDriver.
Follow-up 1
Why is it important?
Encapsulation in Page Object Model is important for several reasons:
Modularity: It allows us to separate the test logic from the page structure and implementation. This makes the code more modular and easier to maintain.
Reusability: Page Objects can be reused across multiple tests or test suites. This saves time and effort in writing and maintaining duplicate code.
Readability: By encapsulating the page elements and actions in a Page Object, the test code becomes more readable and self-explanatory. It improves the overall readability and understandability of the test code.
Maintenance: When the structure or implementation of a web page changes, we only need to update the corresponding Page Object. This reduces the impact of changes on the test code and improves the maintainability of the codebase.
Follow-up 2
How does it improve the maintainability of the code?
Encapsulation in Page Object Model improves the maintainability of the code in several ways:
Centralized Updates: When the structure or implementation of a web page changes, we only need to update the corresponding Page Object. This reduces the impact of changes on the test code and makes it easier to maintain.
Code Reusability: Page Objects can be reused across multiple tests or test suites. This saves time and effort in writing and maintaining duplicate code. It also ensures consistency in the way we interact with the page elements.
Modularity: By separating the test logic from the page structure and implementation, the code becomes more modular. This makes it easier to understand, debug, and enhance.
Readability: The use of Page Objects makes the test code more readable and self-explanatory. It improves the overall readability and understandability of the codebase, making it easier for other team members to collaborate and contribute.
Follow-up 3
Can you provide an example where you used encapsulation in Page Object Model?
Sure! Here's an example of how encapsulation can be used in Page Object Model:
from selenium import webdriver
from selenium.webdriver.common.by import By
class LoginPage:
def __init__(self, driver):
self.driver = driver
self.username_input = (By.ID, 'username')
self.password_input = (By.ID, 'password')
self.login_button = (By.ID, 'login-button')
def enter_username(self, username):
self.driver.find_element(*self.username_input).send_keys(username)
def enter_password(self, password):
self.driver.find_element(*self.password_input).send_keys(password)
def click_login_button(self):
self.driver.find_element(*self.login_button).click()
# Usage:
driver = webdriver.Chrome()
login_page = LoginPage(driver)
login_page.enter_username('my_username')
login_page.enter_password('my_password')
login_page.click_login_button()
Live mock interview
Mock interview: Page Object Model
- Read your scene and goals
- Talk it out; goals tick off live
- Get a score and stronger lines
Your voice and your AI key never touch our servers; the key stays in this browser and is sent only to Google. Only your round scores are saved to track progress.