# How to Implement Page Object Model (POM) with CommonPageCRM Base Class in Selenium

> Learn to implement Page Object Model (POM) with a CommonPageCRM base class in Selenium. Centralize shared CRM elements and actions for efficient test automation.

- Repository: [Anh Tester/automationframeworkselenium](https://github.com/anhtester/automationframeworkselenium)
- Tags: how-to-guide
- Published: 2026-02-24

---

**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`](https://github.com/anhtester/automationframeworkselenium/blob/main/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`](https://github.com/anhtester/automationframeworkselenium/blob/main/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:

```java
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:

```java
// 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:

```java
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:

```java
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`](https://github.com/anhtester/automationframeworkselenium/blob/main/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`](https://github.com/anhtester/automationframeworkselenium/blob/main/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`](https://github.com/anhtester/automationframeworkselenium/blob/main/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`](https://github.com/anhtester/automationframeworkselenium/blob/main/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`](https://github.com/anhtester/automationframeworkselenium/blob/main/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`](https://github.com/anhtester/automationframeworkselenium/blob/main/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`](https://github.com/anhtester/automationframeworkselenium/blob/main/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.