How to Implement Page Object Model (POM) with CommonPageCRM Base Class in Selenium
The CommonPageCRM base class centralizes shared CRM UI elements and lazy-loaded page getters, allowing concrete page classes to inherit common navigation utilities while encapsulating page-specific locators and actions.
The anhtester/automationframeworkselenium repository demonstrates a robust implementation of the Page Object Model (POM) pattern for CRM test automation. By leveraging the CommonPageCRM base class located in src/test/java/com/anhtester/projects/crm/pages/CommonPageCRM.java, the framework isolates UI interactions into reusable page objects that extend this common foundation. This architecture ensures that shared elements like the account dropdown and sign-out functionality are defined once and inherited by all concrete page classes, while lazy initialization optimizes memory usage during test execution.
Understanding the CommonPageCRM Architecture
The framework organizes its page objects into three distinct layers that separate concerns between shared infrastructure, page-specific behavior, and test orchestration.
Base Page Responsibilities
The CommonPageCRM class serves as the foundation for all CRM page objects. It defines shared UI elements—such as the account dropdown and sign-out button—and provides lazy-loaded getter methods like getClientPage() and getDashboardPage(). These getters instantiate page objects only when first accessed, reducing memory overhead and enabling fluid navigation chains in test code.
Concrete Page Implementation
Concrete pages such as SignInPageCRM, DashboardPageCRM, and ClientPageCRM extend CommonPageCRM to inherit its utilities while declaring their own public By locators (e.g., inputEmail, buttonSignIn). Rather than interacting directly with Selenium WebDriver, these classes delegate actions to the com.anhtester.keywords.WebUI keyword library, keeping page objects focused on what to do rather than how to do it.
Test Layer Integration
Test classes extend BaseTest (located in src/test/java/com/anhtester/common/BaseTest.java) to handle driver lifecycle management. Tests instantiate page objects and invoke high-level methods, passing test data from Excel via ExcelHelpers as Hashtable<String,String> objects.
Creating a Concrete Page Class
When adding new functionality to the CRM, create a class that extends CommonPageCRM and declares page-specific locators as public fields. The following example illustrates the pattern used throughout the repository:
package com.anhtester.projects.crm.pages.Reports;
import com.anhtester.projects.crm.pages.CommonPageCRM;
import org.openqa.selenium.By;
/**
* Example page for the “Reports” section of the CRM.
*/
public class ReportPageCRM extends CommonPageCRM {
// Page-specific locators
public By menuReports = By.xpath("//span[normalize-space()='Reports']");
public By buttonGenerate = By.xpath("//button[normalize-space()='Generate']");
public ReportPageCRM() {
super(); // Inherit shared utilities from CommonPageCRM
}
/** Navigate to the Reports page from the dashboard */
public ReportPageCRM openReportPage() {
clickElement(menuReports);
return this;
}
/** Generate a report and wait for download */
public void generateReport() {
clickElement(buttonGenerate);
waitForPageLoaded(); // Keyword from WebUI library
}
}
The clickElement and waitForPageLoaded methods are inherited from the WebUI keyword abstraction layer, ensuring consistent wait strategies and logging across all pages.
Implementing Lazy-Loaded Navigation
The CommonPageCRM base class implements lazy initialization to defer object creation until runtime. This pattern is exposed through getter methods that cache instances:
// Inside CommonPageCRM - provides access to ClientPageCRM
public ClientPageCRM getClientPage() {
if (clientPage == null) {
clientPage = new ClientPageCRM();
}
return clientPage;
}
This design enables fluent interface patterns in test code, allowing method chaining across pages without manual instantiation:
new SignInPageCRM()
.signInWithAdminRole()
.getClientPage()
.openClientTabPage()
.addClient(data);
Writing Tests with the Page Object Model
Test classes leverage the inheritance hierarchy to execute end-to-end workflows. The following test demonstrates signing in, navigating via the base class getters, and passing Excel-derived data to page methods:
package com.anhtester.projects.crm.testcases;
import com.anhtester.projects.crm.pages.SignIn.SignInPageCRM;
import com.anhtester.projects.crm.pages.Dashboard.DashboardPageCRM;
import com.anhtester.projects.crm.pages.Clients.ClientPageCRM;
import com.anhtester.common.BaseTest;
import org.testng.annotations.Test;
import java.util.Hashtable;
public class ClientWorkflowTest extends BaseTest {
@Test
public void addNewClientAsAdmin() {
// Load test data from Excel
Hashtable<String, String> data = ExcelHelpers.getData("Clients", "AddNew");
// Sign in and obtain dashboard via fluent navigation
DashboardPageCRM dashboard = new SignInPageCRM()
.signInWithAdminRole();
// Access ClientPageCRM through lazy-loaded getter
ClientPageCRM clientPage = dashboard
.getClientPage()
.openClientTabPage();
// Execute business action with data-driven input
clientPage.addClient(data);
}
}
The addClient method maps the Hashtable values to UI fields using model classes (e.g., ClientModel.getCompanyName()), ensuring that page objects remain decoupled from hard-coded test data.
Key Files in the Framework
The following source files define the core components of the POM implementation with the CommonPageCRM base class:
src/test/java/com/anhtester/projects/crm/pages/CommonPageCRM.java— Base class providing shared UI elements, sign-out logic, and lazy getters for all CRM pages.src/test/java/com/anhtester/projects/crm/pages/SignIn/SignInPageCRM.java— Concrete page handling authentication actions and verification.src/test/java/com/anhtester/projects/crm/pages/Dashboard/DashboardPageCRM.java— Entry point after login; offers navigation to other modules via inherited methods.src/test/java/com/anhtester/projects/crm/pages/Clients/ClientPageCRM.java— Module-specific page extending the base class to implement CRUD operations.src/main/java/com/anhtester/keywords/WebUI.java— Keyword library wrapping Selenium WebDriver; all page actions delegate here for consistency.src/test/java/com/anhtester/common/BaseTest.java— TestNG base class managing driver setup and teardown for all test cases.
Summary
- Extend CommonPageCRM for all new CRM pages to inherit shared navigation, sign-out functionality, and lazy-loaded getters.
- Declare page-specific locators as public By fields and delegate all interactions to the WebUI keyword library to separate page structure from driver logic.
- Use the base class getter methods (
getClientPage(),getDashboardPage(), etc.) for lazy initialization and to enable fluent test flows. - Pass test data from Excel as Hashtable<String,String> to page methods, using model classes to map keys to UI fields and keep business logic isolated.
Frequently Asked Questions
What is the purpose of the CommonPageCRM base class?
The CommonPageCRM class centralizes shared UI elements—such as the account dropdown and sign-out button—and provides lazy-loaded getter methods for all CRM page objects. Located in src/test/java/com/anhtester/projects/crm/pages/CommonPageCRM.java, it ensures that common navigation utilities are inherited by every concrete page class, eliminating code duplication and simplifying maintenance when shared components change.
How does lazy loading work in this Page Object Model?
The base class implements lazy initialization through getter methods such as getClientPage() and getDashboardPage(), which instantiate page objects only when first called and cache the reference for reuse. This pattern minimizes memory overhead during test execution and allows fluid method chaining in tests, such as dashboard.getClientPage().openClientTabPage(), without requiring explicit constructor calls in the test code.
Where should element locators be stored in the anhtester framework?
Each concrete page class declares its own By locators as public fields (for example, public By menuReports = By.xpath("//span[normalize-space()='Reports']");), while shared locators reside in CommonPageCRM. All locators are strictly encapsulated within page objects; test classes never reference raw XPath or CSS selectors directly, ensuring that UI changes require updates only in the corresponding page class.
How does the framework handle test data separation?
Test data is stored in Excel files and retrieved via the ExcelHelpers utility class, then passed to page methods as Hashtable<String,String> objects. Page objects map these data keys to UI fields using model classes (such as ClientModel.getCompanyName()), which keeps the page objects focused on UI interaction logic while keeping test inputs separate and data-driven.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →