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:

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →