Creating Page Classes in Selenium WebDriver
Creating Page Classes is an important part of implementing the Page Object Model (POM) in Selenium automation. A Page Class represents a particular web page or a meaningful page component of an application and contains the locators and reusable methods required to interact with that page.
Instead of writing Selenium locators and browser interactions directly inside every test class, we create dedicated Page Classes. This keeps test cases clean, reduces code duplication, and makes the automation framework easier to maintain when the application's UI changes. Selenium's official documentation describes Page Objects as a way to model application pages as objects and keep page-specific implementation details in one place. :contentReference[oaicite:0]{index=0}
Course Resource: Selenium Training | Register for Course Demo
1. What is a Page Class?
A Page Class is a Java class created to represent a specific web page or page component of an application. It contains the locators of elements available on that page and methods that perform user actions on those elements.
For example, if an application contains a Login Page, Home Page, Product Page, Cart Page, and Checkout Page, separate Page Classes can be created for each major page.
LoginPage.java
HomePage.java
ProductsPage.java
CartPage.java
CheckoutPage.java
The test class then calls methods from these Page Classes instead of directly interacting with Selenium WebDriver elements.
2. Why Create Page Classes?
Creating Page Classes provides a clear separation between test logic and application UI interaction logic.
- Reduces duplicate Selenium code.
- Centralizes page locators.
- Improves test readability.
- Makes maintenance easier.
- Improves code reusability.
- Separates test logic from UI implementation.
- Makes UI changes easier to manage.
- Supports scalable automation frameworks.
- Works effectively with TestNG, JUnit, and other testing frameworks.
- Works well with Data Providers and external test data.
Selenium specifically highlights separation between test code and page-specific code as a major advantage of Page Objects. :contentReference[oaicite:1]{index=1}
3. Page Class Architecture
A typical Selenium framework separates test classes, Page Classes, utilities, and WebDriver management.
Test Class
|
v
Page Class
|
+-- Locators
|
+-- Page Methods
|
v
WebDriver
|
v
Web Application
The test class describes what should be tested, while the Page Class describes how the application page should be interacted with.
4. Basic Page Class Structure
A simple Page Class generally contains four important parts:
- WebDriver reference.
- Element locators.
- Constructor.
- Reusable page methods.
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class LoginPage {
WebDriver driver;
By username = By.id("username");
By password = By.id("password");
By loginButton = By.id("loginButton");
public LoginPage(WebDriver driver) {
this.driver = driver;
}
public void login(String user, String pass) {
driver.findElement(username).sendKeys(user);
driver.findElement(password).sendKeys(pass);
driver.findElement(loginButton).click();
}
}
5. WebDriver in a Page Class
The Page Class needs access to the WebDriver instance because Selenium uses WebDriver to locate and interact with browser elements.
private WebDriver driver;
public LoginPage(WebDriver driver) {
this.driver = driver;
}
The driver is normally created by a test base class, driver factory, or setup method and then passed to the Page Class constructor.
6. Page Class Constructor
The constructor is used to initialize the Page Class with the WebDriver instance.
public LoginPage(WebDriver driver) {
this.driver = driver;
}
This allows all methods inside the Page Class to use the same browser session.
A constructor can also perform a lightweight page-load check when the Page Object is created. Selenium's documentation shows this pattern as a way to verify that the expected page has been loaded, while keeping ordinary test assertions in the test class. :contentReference[oaicite:2]{index=2}
7. Creating Locators in a Page Class
Locators identify elements on the web page. They should normally be declared inside the Page Class rather than repeatedly written inside test methods.
private By username = By.id("username");
private By password = By.id("password");
private By loginButton = By.id("loginButton");
Common Selenium locator strategies include:
| Locator | Example | Typical Use |
| id | By.id("username") | Unique element ID |
| name | By.name("email") | Name attribute |
| className | By.className("login-btn") | CSS class |
| tagName | By.tagName("button") | HTML tag |
| linkText | By.linkText("Login") | Exact link text |
| cssSelector | By.cssSelector("#username") | CSS selector |
| xpath | By.xpath("//input[@id='username']") | Complex element identification |
8. Using Private Locators
Locators should generally be hidden inside the Page Class using access modifiers such as private. Tests should interact with the page through meaningful methods rather than directly accessing locator variables.
private By usernameField = By.id("username");
private By passwordField = By.id("password");
private By loginButton = By.id("loginButton");
This improves encapsulation because the test does not need to know how a page element is located.
9. Creating Page Methods
Page methods represent actions or services that a user can perform on a page.
public void enterUsername(String username) {
driver.findElement(usernameField).sendKeys(username);
}
public void enterPassword(String password) {
driver.findElement(passwordField).sendKeys(password);
}
public void clickLogin() {
driver.findElement(loginButton).click();
}
These methods allow the test class to interact with the page without knowing the underlying locators.
10. Creating a Complete LoginPage Class
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class LoginPage {
private WebDriver driver;
private By usernameField = By.id("username");
private By passwordField = By.id("password");
private By loginButton = By.id("loginButton");
public LoginPage(WebDriver driver) {
this.driver = driver;
}
public void enterUsername(String username) {
driver.findElement(usernameField).sendKeys(username);
}
public void enterPassword(String password) {
driver.findElement(passwordField).sendKeys(password);
}
public void clickLogin() {
driver.findElement(loginButton).click();
}
public void login(String username, String password) {
enterUsername(username);
enterPassword(password);
clickLogin();
}
}
11. Using the Page Class in a Test
Once the Page Class is created, the test class can create an object of the Page Class and call its methods.
import org.testng.annotations.Test;
public class LoginTest {
@Test
public void validLoginTest() {
LoginPage loginPage = new LoginPage(driver);
loginPage.login("admin", "admin123");
}
}
The test is now easier to read because it describes the user action instead of exposing every Selenium interaction.
12. Before Page Class Design
Without Page Classes, test code can become tightly coupled with the application's HTML structure.
@Test
public void loginTest() {
driver.findElement(By.id("username"))
.sendKeys("admin");
driver.findElement(By.id("password"))
.sendKeys("admin123");
driver.findElement(By.id("loginButton"))
.click();
}
If the username locator changes, every test containing that locator may need to be updated.
13. After Creating a Page Class
With a Page Class, the locator and interaction logic are centralized.
@Test
public void loginTest() {
LoginPage loginPage = new LoginPage(driver);
loginPage.login("admin", "admin123");
}
If the username locator changes, the change can normally be made inside LoginPage rather than throughout multiple tests. This centralized maintenance is one of the primary reasons Selenium recommends Page Objects. :contentReference[oaicite:3]{index=3}
14. Creating a HomePage Class
After successful login, the application may navigate to a Home Page. A separate Page Class can represent that page.
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class HomePage {
private WebDriver driver;
private By welcomeMessage = By.id("welcomeMessage");
private By logoutButton = By.id("logout");
public HomePage(WebDriver driver) {
this.driver = driver;
}
public String getWelcomeMessage() {
return driver.findElement(welcomeMessage).getText();
}
public void logout() {
driver.findElement(logoutButton).click();
}
}
15. Returning Another Page Class
When an action causes navigation to another page, the Page Class method can return the Page Class representing the destination.
public HomePage login(String username, String password) {
driver.findElement(usernameField).sendKeys(username);
driver.findElement(passwordField).sendKeys(password);
driver.findElement(loginButton).click();
return new HomePage(driver);
}
The test can then follow the application's user journey.
LoginPage loginPage = new LoginPage(driver);
HomePage homePage = loginPage.login("admin", "admin123");
System.out.println(homePage.getWelcomeMessage());
This approach models the application flow through Page Objects and can make page transitions explicit in the test code. :contentReference[oaicite:4]{index=4}
16. Creating a ProductsPage Class
A Products Page may contain product names, prices, add-to-cart buttons, filters, and sorting controls.
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class ProductsPage {
private WebDriver driver;
private By searchBox = By.id("search");
private By searchButton = By.id("searchButton");
private By firstProduct = By.cssSelector(".product-item");
public ProductsPage(WebDriver driver) {
this.driver = driver;
}
public void searchProduct(String productName) {
driver.findElement(searchBox).clear();
driver.findElement(searchBox).sendKeys(productName);
driver.findElement(searchButton).click();
}
public void clickFirstProduct() {
driver.findElement(firstProduct).click();
}
}
17. Creating a CartPage Class
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class CartPage {
private WebDriver driver;
private By cartItems = By.cssSelector(".cart-item");
private By checkoutButton = By.id("checkout");
public CartPage(WebDriver driver) {
this.driver = driver;
}
public int getCartItemCount() {
return driver.findElements(cartItems).size();
}
public CheckoutPage clickCheckout() {
driver.findElement(checkoutButton).click();
return new CheckoutPage(driver);
}
}
18. Creating a CheckoutPage Class
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class CheckoutPage {
private WebDriver driver;
private By firstName = By.id("firstName");
private By lastName = By.id("lastName");
private By address = By.id("address");
private By placeOrder = By.id("placeOrder");
public CheckoutPage(WebDriver driver) {
this.driver = driver;
}
public void enterCustomerDetails(
String firstNameValue,
String lastNameValue,
String addressValue) {
driver.findElement(firstName).sendKeys(firstNameValue);
driver.findElement(lastName).sendKeys(lastNameValue);
driver.findElement(address).sendKeys(addressValue);
}
public void placeOrder() {
driver.findElement(placeOrder).click();
}
}
19. Page Class Naming Convention
Page Class names should clearly represent the page or component they model.
| Application Page | Recommended Class |
| Login Page | LoginPage |
| Home Page | HomePage |
| Product Page | ProductPage |
| Products Page | ProductsPage |
| Shopping Cart | CartPage |
| Checkout Page | CheckoutPage |
| User Profile | ProfilePage |
| Admin Dashboard | AdminDashboardPage |
20. Page Class Naming Best Practice
Use clear and consistent names. Avoid generic names such as Page1, TestPage, or WebPage.
Good:
LoginPage
ProductsPage
CheckoutPage
Avoid:
Page1
TestPage
MyPage
WebPage2
21. Page Methods Should Represent User Actions
Page methods should ideally represent meaningful application actions rather than exposing low-level Selenium operations.
For example, instead of writing:
loginPage.clickButton();
loginPage.enterText();
loginPage.enterPassword();
a higher-level method can represent the complete user action:
loginPage.login("admin", "admin123");
This makes test cases easier to understand and keeps page-specific implementation inside the Page Class.
22. Avoid Exposing Page Internals
The Page Class should hide implementation details such as locators whenever possible.
Instead of:
loginPage.usernameField.sendKeys("admin");
prefer:
loginPage.enterUsername("admin");
This keeps the test independent from the actual HTML structure.
23. Page Classes and Encapsulation
Encapsulation means keeping the internal implementation of an object hidden and exposing only the operations that other classes need.
public class LoginPage {
private By username = By.id("username");
public void enterUsername(String value) {
driver.findElement(username).sendKeys(value);
}
}
The test does not need to know that the username field uses an ID locator.
24. Page Classes with Explicit Waits
Dynamic applications may require explicit waits before interacting with elements. Wait logic can be encapsulated within Page Class methods when it is specific to page behavior.
import java.time.Duration;
import org.openqa.selenium.support.ui.WebDriverWait;
public void clickLogin() {
WebDriverWait wait =
new WebDriverWait(driver, Duration.ofSeconds(10));
wait.until(
d -> d.findElement(loginButton).isEnabled()
);
driver.findElement(loginButton).click();
}
This keeps synchronization logic close to the page interaction that requires it.
25. Creating a BasePage Class
Large frameworks may contain common functionality shared by multiple Page Classes. A BasePage can be used to centralize reusable functionality.
import org.openqa.selenium.WebDriver;
public class BasePage {
protected WebDriver driver;
public BasePage(WebDriver driver) {
this.driver = driver;
}
public String getPageTitle() {
return driver.getTitle();
}
public String getCurrentUrl() {
return driver.getCurrentUrl();
}
}
26. Extending BasePage
Page Classes can extend BasePage to reuse common functionality.
public class LoginPage extends BasePage {
private By username = By.id("username");
private By password = By.id("password");
private By loginButton = By.id("loginButton");
public LoginPage(WebDriver driver) {
super(driver);
}
public void login(String user, String pass) {
driver.findElement(username).sendKeys(user);
driver.findElement(password).sendKeys(pass);
driver.findElement(loginButton).click();
}
}
27. Creating a BasePage with Common Actions
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class BasePage {
protected WebDriver driver;
public BasePage(WebDriver driver) {
this.driver = driver;
}
protected void click(By locator) {
driver.findElement(locator).click();
}
protected void type(By locator, String text) {
driver.findElement(locator).clear();
driver.findElement(locator).sendKeys(text);
}
protected String getText(By locator) {
return driver.findElement(locator).getText();
}
protected boolean isDisplayed(By locator) {
return driver.findElement(locator).isDisplayed();
}
}
Common methods should be kept focused. Avoid turning BasePage into a large collection of unrelated business logic.
28. LoginPage Using BasePage
public class LoginPage extends BasePage {
private By username = By.id("username");
private By password = By.id("password");
private By loginButton = By.id("loginButton");
public LoginPage(WebDriver driver) {
super(driver);
}
public void enterUsername(String value) {
type(username, value);
}
public void enterPassword(String value) {
type(password, value);
}
public HomePage login(String user, String pass) {
type(username, user);
type(password, pass);
click(loginButton);
return new HomePage(driver);
}
}
29. Creating Page Classes with PageFactory
Selenium provides PageFactory, which can be used to initialize Page Object fields and supports annotations such as @FindBy. The Selenium Java API documents PageFactory as a factory that simplifies working with Page Objects and initializes WebElement fields using a SearchContext. :contentReference[oaicite:5]{index=5}
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 {
private WebDriver driver;
@FindBy(id = "username")
private WebElement username;
@FindBy(id = "password")
private WebElement password;
@FindBy(id = "loginButton")
private WebElement loginButton;
public LoginPage(WebDriver driver) {
this.driver = driver;
PageFactory.initElements(driver, this);
}
public void login(String user, String pass) {
username.sendKeys(user);
password.sendKeys(pass);
loginButton.click();
}
}
30. @FindBy Annotation
The @FindBy annotation allows an element to be described using Selenium locator strategies.
@FindBy(id = "username")
private WebElement username;
Other examples include:
@FindBy(name = "email")
private WebElement email;
@FindBy(css = ".login-button")
private WebElement loginButton;
@FindBy(xpath = "//button[@type='submit']")
private WebElement submitButton;
Selenium's PageFactory API supports locator annotations including FindBy-related mechanisms. :contentReference[oaicite:6]{index=6}
31. By Locators vs @FindBy
| By Locators | @FindBy |
| Uses By objects | Uses WebElement fields |
| Explicit findElement calls | Element fields are initialized through PageFactory |
| Simple and direct | Convenient for annotation-based Page Objects |
| Works without PageFactory | Commonly used with PageFactory |
32. Creating Page Classes Without PageFactory
PageFactory is not required to implement the Page Object Model. Page Classes can simply store By locators and use WebDriver methods.
public class LoginPage {
private WebDriver driver;
private By username = By.id("username");
public LoginPage(WebDriver driver) {
this.driver = driver;
}
public void enterUsername(String value) {
driver.findElement(username).sendKeys(value);
}
}
This approach is straightforward and keeps the locator definition explicit.
33. Page Classes and TestNG
Page Classes can be used directly with TestNG test methods.
import org.testng.Assert;
import org.testng.annotations.Test;
public class LoginTest {
@Test
public void validLoginTest() {
LoginPage loginPage = new LoginPage(driver);
HomePage homePage =
loginPage.login("admin", "admin123");
Assert.assertTrue(
homePage.getWelcomeMessage().contains("Welcome")
);
}
}
The test contains the validation, while the Page Classes contain page interaction logic. Selenium's guidance recommends keeping test assertions in tests rather than placing normal assertions inside Page Objects. :contentReference[oaicite:7]{index=7}
34. Page Classes and Data Providers
Data Providers can supply multiple test-data combinations while Page Classes handle the application interactions.
@DataProvider(name = "loginData")
public Object[][] loginData() {
return new Object[][] {
{"admin", "admin123"},
{"manager", "manager123"},
{"employee", "employee123"}
};
}
@Test(dataProvider = "loginData")
public void loginTest(String username, String password) {
LoginPage loginPage = new LoginPage(driver);
loginPage.login(username, password);
}
This creates a clean separation:
DataProvider
|
v
Test Data
|
v
Test Method
|
v
Page Class
|
v
Selenium WebDriver
35. Page Classes and Assertions
Page Classes should generally provide information that the test can verify instead of performing the test's assertions themselves.
For example:
public String getWelcomeMessage() {
return driver.findElement(welcomeMessage).getText();
}
The test performs the assertion:
String message = homePage.getWelcomeMessage();
Assert.assertTrue(
message.contains("Welcome")
);
This maintains a clean separation between page behavior and test verification. Selenium's official guidance explicitly recommends that ordinary assertions belong in test code rather than Page Objects. :contentReference[oaicite:8]{index=8}
36. Page Classes and Navigation
A Page Class can return another Page Class when an action navigates to a different page.
public ProductsPage clickProducts() {
driver.findElement(productsMenu).click();
return new ProductsPage(driver);
}
The test can then continue the workflow:
HomePage homePage = new HomePage(driver);
ProductsPage productsPage =
homePage.clickProducts();
productsPage.searchProduct("Laptop");
37. Modeling Successful and Failed Actions
The same action may have different expected outcomes. For example, a valid login may navigate to HomePage while an invalid login may remain on LoginPage and display an error.
public HomePage loginSuccessfully(
String username,
String password) {
enterUsername(username);
enterPassword(password);
clickLogin();
return new HomePage(driver);
}
public LoginPage loginExpectingFailure(
String username,
String password) {
enterUsername(username);
enterPassword(password);
clickLogin();
return new LoginPage(driver);
}
This makes the expected application flow explicit in the Page Class API. Selenium's Page Object guidance describes this pattern for actions that can result in different pages or states. :contentReference[oaicite:9]{index=9}
38. Page Class for Search
public class SearchPage {
private WebDriver driver;
private By searchBox = By.id("searchBox");
private By searchButton = By.id("searchButton");
private By results = By.cssSelector(".search-result");
public SearchPage(WebDriver driver) {
this.driver = driver;
}
public void search(String keyword) {
driver.findElement(searchBox).clear();
driver.findElement(searchBox).sendKeys(keyword);
driver.findElement(searchButton).click();
}
public int getResultCount() {
return driver.findElements(results).size();
}
}
39. Page Class for Registration
public class RegistrationPage {
private WebDriver driver;
private By firstName = By.id("firstName");
private By lastName = By.id("lastName");
private By email = By.id("email");
private By password = By.id("password");
private By registerButton = By.id("register");
public RegistrationPage(WebDriver driver) {
this.driver = driver;
}
public void register(
String first,
String last,
String emailValue,
String passwordValue) {
driver.findElement(firstName).sendKeys(first);
driver.findElement(lastName).sendKeys(last);
driver.findElement(email).sendKeys(emailValue);
driver.findElement(password).sendKeys(passwordValue);
driver.findElement(registerButton).click();
}
}
40. Page Class for User Profile
public class ProfilePage {
private WebDriver driver;
private By profileName = By.id("profileName");
private By email = By.id("email");
private By saveButton = By.id("save");
public ProfilePage(WebDriver driver) {
this.driver = driver;
}
public void updateName(String name) {
driver.findElement(profileName).clear();
driver.findElement(profileName).sendKeys(name);
}
public void updateEmail(String value) {
driver.findElement(email).clear();
driver.findElement(email).sendKeys(value);
}
public void saveChanges() {
driver.findElement(saveButton).click();
}
}
41. Page Classes and Reusable Components
A complex page may contain reusable components such as navigation bars, product cards, menus, filters, headers, and footers. These can be represented using component objects instead of placing all functionality into one large Page Class.
HomePage
|
+-- Header
|
+-- NavigationMenu
|
+-- ProductSection
|
+-- Footer
Selenium documentation also describes Page Component Objects for representing discrete parts of a page and composing them inside Page Objects. :contentReference[oaicite:10]{index=10}
42. Page Component Example
public class ProductComponent {
private WebElement root;
public ProductComponent(WebElement root) {
this.root = root;
}
public String getProductName() {
return root.findElement(
By.cssSelector(".product-name")
).getText();
}
public void addToCart() {
root.findElement(
By.cssSelector(".add-to-cart")
).click();
}
}
A ProductsPage can then work with multiple ProductComponent objects.
43. Page Class with Product Components
public List<ProductComponent> getProducts() {
List<WebElement> elements =
driver.findElements(
By.cssSelector(".product")
);
return elements.stream()
.map(ProductComponent::new)
.toList();
}
This composition approach can make complex pages easier to model and maintain. :contentReference[oaicite:11]{index=11}
44. Creating a Complete E-Commerce Page Flow
LoginPage
|
v
HomePage
|
v
ProductsPage
|
v
ProductDetailsPage
|
v
CartPage
|
v
CheckoutPage
|
v
OrderConfirmationPage
Each Page Class represents a meaningful stage of the user's journey through the application.
45. Complete E-Commerce Test Example
@Test
public void purchaseProductTest() {
LoginPage loginPage =
new LoginPage(driver);
HomePage homePage =
loginPage.login("admin", "admin123");
ProductsPage productsPage =
homePage.clickProducts();
productsPage.searchProduct("Laptop");
ProductDetailsPage productPage =
productsPage.clickFirstProduct();
productPage.addToCart();
CartPage cartPage =
productPage.openCart();
CheckoutPage checkoutPage =
cartPage.clickCheckout();
checkoutPage.enterCustomerDetails(
"John",
"Smith",
"Mumbai"
);
checkoutPage.placeOrder();
}
The test reads almost like a user journey because the Page Classes hide low-level Selenium implementation details.
46. Page Class and Wait Strategy
Dynamic web applications often require synchronization. Page Classes can encapsulate page-specific wait behavior.
public void waitForLoginButton() {
WebDriverWait wait =
new WebDriverWait(
driver,
Duration.ofSeconds(10)
);
wait.until(
ExpectedConditions.elementToBeClickable(
loginButton
)
);
}
Waits should be used deliberately rather than adding unnecessary fixed delays such as Thread.sleep().
47. Avoiding Thread.sleep()
Hard-coded sleeps can make automation slower and less reliable because they wait for a fixed amount of time regardless of whether the page is ready.
Instead of:
Thread.sleep(5000);
driver.findElement(loginButton).click();
prefer an explicit wait:
WebDriverWait wait =
new WebDriverWait(driver, Duration.ofSeconds(10));
wait.until(
ExpectedConditions.elementToBeClickable(loginButton)
).click();
48. Page Class Validation
A Page Class may perform a lightweight check to ensure that the correct page has been loaded.
public LoginPage(WebDriver driver) {
this.driver = driver;
if (!driver.getTitle().contains("Login")) {
throw new IllegalStateException(
"Expected Login Page but current page is: "
+ driver.getCurrentUrl()
);
}
}
This type of page-load verification is different from ordinary test assertions. Selenium's documentation specifically notes that checking whether the expected page has loaded can be appropriate when creating a Page Object. :contentReference[oaicite:12]{index=12}
49. Page Classes Should Not Contain Test Assertions
A common design mistake is putting assertions directly into every Page Class method.
Avoid:
public void verifyLogin() {
Assert.assertEquals(
driver.getTitle(),
"Dashboard"
);
}
Prefer returning information:
public String getPageTitle() {
return driver.getTitle();
}
Then verify it in the test:
Assert.assertEquals(
homePage.getPageTitle(),
"Dashboard"
);
50. Page Classes and Encapsulated Business Actions
Page methods can represent meaningful business-level operations.
public void completeLogin(
String username,
String password) {
enterUsername(username);
enterPassword(password);
clickLogin();
}
Instead of exposing every technical step to the test, the Page Class provides a reusable service.
51. Page Class Method Return Types
Page methods can return different types depending on the operation.
| Return Type | Typical Purpose |
| void | Action with no useful return value |
| String | Return text, title, or value |
| boolean | Return a condition or state |
| int | Return count or numeric value |
| Page Class | Navigate to another page |
| Component Class | Return a page component |
52. Fluent Page Class Methods
A Page Class can use a fluent style where methods return the current Page Object or another Page Object.
public LoginPage enterUsername(String username) {
driver.findElement(usernameField)
.sendKeys(username);
return this;
}
public LoginPage enterPassword(String password) {
driver.findElement(passwordField)
.sendKeys(password);
return this;
}
The test can then write:
loginPage
.enterUsername("admin")
.enterPassword("admin123")
.clickLogin();
Selenium documents fluent APIs as one possible design approach for Page Objects. :contentReference[oaicite:13]{index=13}
53. Page Class Package Structure
A common project structure separates Page Classes from tests and utilities.
src
|-- test
|-- java
|-- tests
| |-- LoginTest.java
| |-- ProductTest.java
| |-- CheckoutTest.java
|
|-- pages
| |-- LoginPage.java
| |-- HomePage.java
| |-- ProductsPage.java
| |-- ProductDetailsPage.java
| |-- CartPage.java
| |-- CheckoutPage.java
|
|-- components
| |-- ProductComponent.java
| |-- HeaderComponent.java
|
|-- utilities
|-- DriverFactory.java
|-- ConfigReader.java
|-- ExcelReader.java
54. Page Classes with DriverFactory
In a scalable framework, WebDriver creation can be separated from Page Classes.
public class DriverFactory {
private static WebDriver driver;
public static WebDriver createDriver() {
driver = new ChromeDriver();
return driver;
}
public static WebDriver getDriver() {
return driver;
}
}
Page Classes receive the driver instead of creating their own browser session.
WebDriver driver = DriverFactory.createDriver();
LoginPage loginPage =
new LoginPage(driver);
55. Page Class Should Not Usually Create the Browser
A Page Class should normally represent a page rather than manage the complete lifecycle of the browser.
Avoid:
public LoginPage() {
driver = new ChromeDriver();
}
Prefer:
public LoginPage(WebDriver driver) {
this.driver = driver;
}
This allows the same Page Class to work with different browser configurations and framework-level WebDriver management.
56. Page Classes and Cross-Browser Testing
Because the Page Class receives a WebDriver instance, the same Page Class can generally work with Chrome, Firefox, Edge, or another supported WebDriver implementation.
WebDriver driver = new ChromeDriver();
LoginPage loginPage = new LoginPage(driver);
or:
WebDriver driver = new FirefoxDriver();
LoginPage loginPage = new LoginPage(driver);
The Page Class does not need to change simply because the browser implementation changes.
57. Page Classes with Parameterized Test Data
@DataProvider(name = "users")
public Object[][] users() {
return new Object[][] {
{"admin", "admin123"},
{"manager", "manager123"},
{"employee", "employee123"}
};
}
@Test(dataProvider = "users")
public void loginTest(
String username,
String password) {
LoginPage loginPage =
new LoginPage(driver);
loginPage.login(username, password);
}
This combination is useful for building reusable data-driven Selenium frameworks.
58. Page Class with Error Message
Negative test cases may need access to validation messages.
private By errorMessage =
By.cssSelector(".login-error");
public String getErrorMessage() {
return driver.findElement(errorMessage)
.getText();
}
The test can then verify the message:
loginPage.loginExpectingFailure(
"invalid",
"wrongPassword"
);
Assert.assertEquals(
loginPage.getErrorMessage(),
"Invalid username or password"
);
59. Page Class for Dropdowns
Page Classes can encapsulate interactions with dropdowns and other complex controls.
private By countryDropdown =
By.id("country");
public void selectCountry(String country) {
Select select =
new Select(
driver.findElement(countryDropdown)
);
select.selectByVisibleText(country);
}
60. Page Class for Checkboxes
private By termsCheckbox =
By.id("terms");
public void acceptTerms() {
WebElement checkbox =
driver.findElement(termsCheckbox);
if (!checkbox.isSelected()) {
checkbox.click();
}
}
61. Page Class for Radio Buttons
private By maleRadio =
By.id("male");
private By femaleRadio =
By.id("female");
public void selectGender(String gender) {
if (gender.equalsIgnoreCase("male")) {
driver.findElement(maleRadio).click();
} else if (gender.equalsIgnoreCase("female")) {
driver.findElement(femaleRadio).click();
}
}
62. Page Class for Tables
Tables can also be represented through Page Class methods.
private By tableRows =
By.cssSelector("table tbody tr");
public int getRowCount() {
return driver.findElements(tableRows).size();
}
For a more complex table, the Page Class can provide methods to retrieve specific rows or values.
63. Page Classes for Reusable Navigation
Navigation components are often shared across multiple pages.
public class HeaderComponent {
private WebDriver driver;
private By homeLink = By.id("home");
private By productsLink = By.id("products");
private By profileLink = By.id("profile");
public HeaderComponent(WebDriver driver) {
this.driver = driver;
}
public HomePage clickHome() {
driver.findElement(homeLink).click();
return new HomePage(driver);
}
public ProductsPage clickProducts() {
driver.findElement(productsLink).click();
return new ProductsPage(driver);
}
}
64. Complete Page Class Architecture
Test Classes
|
v
Page Object Classes
|
+--------------+--------------+
| | |
Locators Page Methods Components
| | |
+--------------+--------------+
|
v
WebDriver
|
v
Web Application
65. Real-World Login Page Class
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class LoginPage {
private final WebDriver driver;
private final By username =
By.id("username");
private final By password =
By.id("password");
private final By loginButton =
By.id("loginButton");
private final By errorMessage =
By.cssSelector(".login-error");
public LoginPage(WebDriver driver) {
this.driver = driver;
}
public LoginPage enterUsername(String value) {
driver.findElement(username)
.clear();
driver.findElement(username)
.sendKeys(value);
return this;
}
public LoginPage enterPassword(String value) {
driver.findElement(password)
.clear();
driver.findElement(password)
.sendKeys(value);
return this;
}
public HomePage clickLogin() {
driver.findElement(loginButton)
.click();
return new HomePage(driver);
}
public HomePage login(
String usernameValue,
String passwordValue) {
enterUsername(usernameValue);
enterPassword(passwordValue);
return clickLogin();
}
public String getErrorMessage() {
return driver.findElement(errorMessage)
.getText();
}
}
66. Complete Test Using the Page Class
import org.testng.Assert;
import org.testng.annotations.Test;
public class LoginTest {
@Test
public void validLoginTest() {
LoginPage loginPage =
new LoginPage(driver);
HomePage homePage =
loginPage.login(
"admin",
"admin123"
);
Assert.assertTrue(
homePage.getWelcomeMessage()
.contains("Welcome")
);
}
}
67. Page Class Design: Good vs Poor
| Good Design | Poor Design |
| Locators are centralized | Locators repeated across tests |
| Methods represent user actions | Tests directly manipulate WebElements |
| Page-specific logic stays in Page Class | Page logic scattered across test classes |
| Tests focus on verification | Tests contain excessive UI implementation details |
| Reusable methods | Duplicated Selenium code |
| Clear Page Class names | Generic class names |
68. Common Mistakes When Creating Page Classes
- Creating one giant Page Class for the entire application.
- Putting assertions everywhere inside Page Classes.
- Making all locators public.
- Duplicating the same locator in multiple classes.
- Creating a new WebDriver inside every Page Class.
- Using Thread.sleep() excessively.
- Putting test-data generation inside page interaction methods.
- Mixing test logic with page implementation.
- Using unclear Page Class names.
- Exposing raw WebElements unnecessarily.
- Creating methods that perform unrelated operations.
- Sharing unsafe WebDriver state across parallel tests.
69. Best Practices for Creating Page Classes
- Use one Page Class for a meaningful page or reusable page component.
- Keep locators inside the relevant Page Class.
- Use private fields for implementation details.
- Use meaningful method names.
- Keep test assertions in test classes.
- Pass WebDriver through the constructor.
- Keep browser creation outside Page Classes.
- Use explicit waits when synchronization is required.
- Return another Page Object when an action navigates to another page.
- Use reusable component objects for repeated UI sections.
- Keep Page Classes focused on page services and behavior.
- Avoid exposing the underlying WebDriver unnecessarily.
- Use consistent package and naming conventions.
- Keep Page Classes small enough to remain understandable.
These practices align with Selenium's Page Object guidance, which emphasizes exposing page services through methods, hiding implementation details, minimizing duplication, and avoiding ordinary assertions inside Page Objects. :contentReference[oaicite:14]{index=14}
70. Practical Project Structure
selenium-project
|
|-- src
| |-- test
| |-- java
| |
| |-- tests
| | |-- LoginTest.java
| | |-- SearchTest.java
| | |-- ProductTest.java
| | |-- CheckoutTest.java
| |
| |-- pages
| | |-- BasePage.java
| | |-- LoginPage.java
| | |-- HomePage.java
| | |-- SearchPage.java
| | |-- ProductsPage.java
| | |-- ProductDetailsPage.java
| | |-- CartPage.java
| | |-- CheckoutPage.java
| |
| |-- components
| | |-- HeaderComponent.java
| | |-- ProductComponent.java
| |
| |-- data
| | |-- LoginDataProvider.java
| | |-- ProductDataProvider.java
| |
| |-- utilities
| |-- DriverFactory.java
| |-- ConfigReader.java
| |-- ExcelReader.java
|
|-- testng.xml
|-- pom.xml
71. Page Classes in a Selenium Automation Framework
TestNG
|
v
Test Classes
|
v
Page Classes
|
+-----------+-----------+
| | |
LoginPage ProductsPage CartPage
| | |
+-----------+-----------+
|
v
WebDriver
|
v
Browser
|
v
Web Application
72. Complete Practical Workflow
- Create the Selenium project.
- Configure WebDriver management.
- Create a pages package.
- Create a BasePage if common functionality is required.
- Create one Page Class for each important application page.
- Identify stable locators for page elements.
- Declare locators inside the corresponding Page Class.
- Create reusable methods for page actions.
- Return destination Page Objects when navigation occurs.
- Create test classes separately.
- Use TestNG Data Providers for multiple test-data combinations when needed.
- Perform assertions in test classes.
- Execute the tests.
- Generate test reports.
73. Page Class vs Test Class
| Page Class | Test Class |
| Contains page locators | Contains test scenarios |
| Contains page interaction methods | Contains assertions |
| Represents application UI | Represents validation requirements |
| Hides Selenium implementation details | Uses Page Class methods |
| Focuses on reusable behavior | Focuses on test verification |
74. Interview Questions on Creating Page Classes
1. What is a Page Class?
A Page Class is a class that represents a web page or reusable page component and contains its locators and interaction methods.
2. Why are Page Classes used in Selenium?
They reduce duplication, improve maintainability, separate test logic from UI implementation, and provide reusable page-level operations.
3. What should a Page Class contain?
It commonly contains the WebDriver reference, locators, constructor, waits where appropriate, and reusable page interaction methods.
4. Should a Page Class contain assertions?
Ordinary test assertions should generally remain in the test class. A Page Object may perform a page-load check when it is instantiated. :contentReference[oaicite:15]{index=15}
5. Why is the WebDriver passed to the Page Class constructor?
It allows the Page Class to use the same browser session managed by the test framework.
6. Should a Page Class create a new WebDriver?
Normally no. WebDriver creation and lifecycle management are better handled by framework-level setup or a DriverFactory.
7. What is the advantage of private locators?
Private locators hide implementation details and encourage tests to use meaningful Page Class methods.
8. What is PageFactory?
PageFactory is a Selenium Java utility that can initialize Page Object fields and is commonly used with annotations such as @FindBy. :contentReference[oaicite:16]{index=16}
9. Can Page Classes return other Page Classes?
Yes. Returning another Page Class is useful when an action navigates to another page.
10. Can Page Classes be used with TestNG?
Yes. TestNG test methods can create Page Class objects and call their reusable methods.
11. Can Page Classes work with Data Providers?
Yes. Data Providers can supply test data while Page Classes handle browser interactions.
12. What is BasePage?
BasePage is a common parent class that can contain reusable functionality shared by multiple Page Classes.
13. What is a Page Component?
A Page Component represents a reusable section of a web page, such as a product card, header, navigation menu, or footer. :contentReference[oaicite:17]{index=17}
14. Why should test classes avoid direct locators?
Keeping locators in Page Classes reduces duplication and isolates tests from changes in the application's HTML structure.
15. What is the main goal of Page Classes?
The main goal is to create a maintainable abstraction between automated tests and the application's user interface.
75. Quick Reference Table
| Concept | Description |
| Page Class | Represents a web page or page component |
| WebDriver | Browser automation interface used by Page Classes |
| Locator | Identifies an element on the page |
| Page Method | Represents an action or service offered by the page |
| BasePage | Contains common functionality for Page Classes |
| PageFactory | Selenium utility for initializing Page Object fields |
| @FindBy | Annotation used to define element lookup information |
| Page Component | Represents a reusable section of a page |
| DataProvider | Supplies multiple test-data combinations |
| Test Class | Contains scenarios and assertions |
76. Learning Roadmap for Creating Page Classes
- Learn Java classes and objects.
- Understand Selenium WebDriver.
- Learn Selenium locator strategies.
- Understand the Page Object Model.
- Create a basic LoginPage.
- Add locators to the Page Class.
- Create reusable page methods.
- Pass WebDriver through constructors.
- Create multiple Page Classes.
- Return Page Objects after navigation.
- Create a BasePage for common functionality.
- Learn PageFactory and @FindBy.
- Create reusable Page Components.
- Integrate Page Classes with TestNG.
- Integrate Page Classes with Data Providers.
- Add explicit waits and synchronization.
- Build a complete POM-based Selenium framework.
77. Practical Exercises
- Create a LoginPage class with username, password, and login button locators.
- Create a HomePage class with welcome message and logout methods.
- Create a SearchPage class for product searches.
- Create a ProductsPage class with product-related actions.
- Create a CartPage class with cart item and checkout methods.
- Create a CheckoutPage class with customer information fields.
- Create a BasePage class with common click, type, and text methods.
- Create a HeaderComponent class for common navigation.
- Use PageFactory with @FindBy to create a LoginPage.
- Create a Data Provider containing multiple login credentials.
- Execute the same LoginPage workflow using different data sets.
- Build a complete e-commerce workflow using multiple Page Classes.
78. Real-World Example: Login Automation
Suppose an application contains a login page with username, password, and login button. Instead of putting all Selenium commands into every test, create a LoginPage.
public class LoginPage {
private WebDriver driver;
private By username =
By.id("username");
private By password =
By.id("password");
private By loginButton =
By.id("loginButton");
public LoginPage(WebDriver driver) {
this.driver = driver;
}
public HomePage login(
String user,
String pass) {
driver.findElement(username)
.sendKeys(user);
driver.findElement(password)
.sendKeys(pass);
driver.findElement(loginButton)
.click();
return new HomePage(driver);
}
}
The test then becomes:
@Test
public void loginTest() {
LoginPage loginPage =
new LoginPage(driver);
HomePage homePage =
loginPage.login(
"admin",
"admin123"
);
Assert.assertTrue(
homePage.getWelcomeMessage()
.contains("Welcome")
);
}
79. Real-World Page Object Architecture
Test Data
|
v
TestNG DataProvider
|
v
Test Class
|
v
LoginPage
|
v
HomePage
|
v
ProductsPage
|
v
CartPage
|
v
CheckoutPage
|
v
WebDriver
|
v
Browser
|
v
Application
80. Summary
Creating Page Classes is a fundamental practice when building a maintainable Selenium automation framework using the Page Object Model. A Page Class represents a web page or meaningful page component and contains its locators and reusable interaction methods.
The WebDriver instance is normally passed into the Page Class constructor, while locators remain encapsulated inside the class. Page methods should represent meaningful user actions such as login, search, checkout, navigation, or form submission.
Tests should generally focus on scenarios and assertions, while Page Classes handle the application's UI interaction details. When navigation occurs, Page methods can return another Page Object to model the application's workflow.
For larger frameworks, Page Classes can be combined with BasePage classes, PageFactory, @FindBy, Page Components, TestNG Data Providers, explicit waits, DriverFactory, Maven, CI/CD, and reporting tools.
Final Takeaway: A well-designed Page Class creates a clean boundary between Selenium implementation and test scenarios. When the application's UI changes, page-specific changes can usually be isolated to the relevant Page Class, reducing duplication and improving framework maintainability. :contentReference[oaicite:18]{index=18}
81. Course Resources