POM in Selenium

Write clean, scalable, and reliable Selenium tests using the power of Page Object Model in Java

Page Object Model in Selenium

Page Object Model in Selenium, Java & Real-Life Chaos

“Why is our test failing again?!”
Been there? Yeah, me too – 3 AM, CI pipeline redder than my eyes.
The Page Object Model (POM) in Selenium can save your butt…
If – and that’s a big IF – you don’t mess it up like I once did.

Let’s break this down like I’m training you over chai in the office pantry, not like a dry-as-dust doc written by AI bots.
We’ll cover what works, what absolutely sucks, and how to build a clean, badass POM structure with Java.

Why Page Object Model (POM) Exists in the First Place

You ever open a Selenium test file and feel your brain melt?

I once inherited a UI test suite where every damn click, sendKeys, and assert lived in the test method itself. 1,000+ lines of pure soup. Zero reuse. I cried.

That’s why POM exists. It’s about:

  • Reusability

  • Readability

  • Maintainability

Write once. Use everywhere.

You move all the web elements and UI actions to separate page classes. Test logic stays clean. Maintenance becomes less painful than debugging JavaScript in IE8 (true story).

Real Talk: POM Gone Wrong

Just because it’s called “Page Object” doesn’t mean you need one class per page. Nope.

I once saw a POM class with 147 methods. It had logic for login, signup, forgot password, search, checkout, feedback form – you name it. All. In. One. File. 🤦‍♂️

POM isn’t a license to dump stuff randomly. It’s not your digital junk drawer.

Wait – I almost forgot… This also includes teams that go full-on OOP overkill:

AbstractLoginBaseFactoryServiceImpl.java – yes, that was real.

Let’s break down what actually makes a good one.

What Makes a Good Page Object Model

✅ Structure
  • One page = one class (most of the time). This keeps it neat.
  • Methods should reflect user actions, not technical steps. This helps anyone who looks at your code immediately understand what’s happening.
  • Keep test logic out of page classes. This is what drives me crazy when I see test data hardcoded in the page class. Keep your page objects simple!
✅ Naming

Use verbs that reflect behaviour:

				
					loginPage.enterUsername("admin");
loginPage.enterPassword("1234");
loginPage.clickLogin();

				
			

Not :

				
					typeText1();
setFieldA();
clickButton1();

				
			
✅ Error Handling
  • Wrap flaky locators with retry logic or wait strategies.
  • Better than random Thread.sleep(). Unless you like failure.

Classic POM vs. PageFactory — What Nobody Tells You

PageFactory sounds fancy. And yes, it was useful once. But here’s the tea:

PageFactory: Pros
  • Cleaner syntax (@FindBy looks nice)
  • Elements are initialized automatically
PageFactory: Cons
  • Lazy loading = surprises at runtime
  • Doesn’t play well with dynamic content
  • People treat @FindBy as a hammer for every nail (and that’s dangerous)
Classic POM: Pros
  • More control (you decide when and how to fetch elements)
  • Easier debugging
Classic POM: Cons
  • Slightly more verbose

Real Advice?
Use PageFactory for static pages. Stick with classic POM for anything dynamic, flaky, or AJAX-heavy.

Also, don’t mix both. It’s like wearing Crocs to a wedding. Just don’t. For a more in-depth understanding of automation testing tools, check out Selenium’s official documentation.

Case Study: Refactoring a Spaghetti Selenium Project

Let me paint a nightmare:

  • Test class with 3,000+ lines
  • Every locator written inline
  • driver.findElement all over the place
  • 20 test cases failing daily — not because the app broke, but because the tests did

 

What we did:

  1. Audit & Group: Collected repeated locators and grouped them into page-specific classes.

  2. Extract Actions: Converted inline code like driver.findElement(...).click() into readable methods like homePage.clickLoginButton();

  3. Add Base Page: Common wait methods, logging, and utility functions moved to BasePage.

  4. Refactor Tests: Test methods went from 50+ lines to 5–10 lines of business logic.

  5. Results: From 20+ flaky tests/day to zero flakiness. Devs started trusting automation again.

Lesson?

Spaghetti code is delicious in Italy. Not in test automation.

A Scalable Page Object Checklist

Want to keep your sanity when your suite grows to 500+ tests?
Here’s your go-to checklist:

✅ Naming
  • Page class: LoginPage, not loginpg
  • Method: clickSubmitButton(), not btnclk()
✅ Design
  • Keep elements private
  • Expose user actions as public methods
  • Never call driver.findElement in test classes
✅ Modularity
  • Separate BasePage for waits, logging, helpers
  • Break big pages into sections if needed (e.g., Header, Sidebar, Footer)
✅ Readability
  • Write tests like user stories

				
					LoginPage login = new LoginPage(driver);
login.enterUsername("admin");
login.enterPassword("secret");
login.clickLogin();

				
			
✅ Maintainability
  • Centralize locators
  • Use constants for timeouts
  • Add logs, screenshots on failure

Print this. Stick it on your desk. Thank me later.

Page Object Model Refactoring: Breaking the Spaghetti Code

Now, let’s talk about refactoring—really refactoring. The kind of refactor that makes you go from headaches to high fives.

I remember one project where I was tasked with refactoring an absolute mess. The code was like a runaway train: unstructured, untestable, and overly complex. Locators and test steps were mashed together, making it impossible to add new tests without breaking the existing ones. Every new test seemed like a patch to the failing code rather than a well-thought-out addition.

The Breakthrough Moment

Here’s the key to refactoring your test suite: start by breaking your pages down into sections. Don’t just create a massive monolithic page object for each entire page. Separate out logical chunks like headers, footers, sidebars, and modals.

Once we broke things into manageable chunks, everything clicked. We could test small, isolated components, reuse code, and ensure consistency across tests. The result? A clean, scalable framework that didn’t feel like a ticking time bomb.

POM Template: Clean and Simple

				
					public class LoginPage extends BasePage {

    private By usernameField = By.id("username");
    private By passwordField = By.id("password");
    private By loginButton = By.id("loginBtn");

    public LoginPage(WebDriver driver) {
        super(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();
    }
}

				
			

Do’s and Don’ts

✅ Do:
  • Think like a user, not a developer
  • Build helper methods in BasePage
  • Use waits smartly (not blindly)
  • Separate page objects and test logic
  • Use constructors wisely
❌ Don’t:
  • Write assertions in page classes
  • Mix test data with element locators
  • Use Thread.sleep() unless you enjoy chaos
  • Repeat yourself (DRY = Don’t Repeat Yourself)
  • Over-engineer — it’s test code, not an OS kernel

FAQs

The Page Object Model (POM) is a design pattern that is commonly used in Selenium-based automation frameworks. The main idea is to create separate classes for each page in your web application. Each page class contains methods representing the actions that can be performed on that page, as well as locators for the page elements. This approach separates test logic from UI interaction, improving maintainability and scalability.

POM brings several benefits to test automation:

  • Maintainability: Changes in the UI (such as element locators) only need to be made in one place — the corresponding page object class.

  • Readability: Your tests become more readable because they reflect the user flow (e.g., loginPage.enterUsername()), making it easier to understand.

  • Reusability: Once you create a page object for a page, it can be reused in multiple test cases, reducing code duplication.

PageFactory is a useful tool that automates the initialization of elements in the page object class using annotations like @FindBy. It can reduce boilerplate code, but it has some limitations, such as being prone to lazy loading and not playing well with dynamic content.

It’s better to use PageFactory for static pages with relatively simple structures. For more complex, dynamic pages, a classic approach where elements are initialized explicitly (e.g., driver.findElement()) might be more reliable.

Dynamic elements can be tricky, especially when their IDs or XPaths change frequently. To handle this:

  • Use dynamic locators based on the text or other attributes (like CSS classes).

  • Implement custom wait strategies to wait for elements to appear or become clickable.

  • Consider using explicit waits to handle elements that take time to load or change.

Yes, you can. Even if your project is small, POM will help you maintain clear separation between your test logic and UI interactions. While it might seem like overkill for a simple test, starting with POM ensures that your code is scalable and easier to maintain as your project grows.

Here are some common mistakes to avoid when using POM:

  • Overloading page objects: Trying to put everything related to a page in a single class, making it overly complex and hard to maintain.

  • Including test logic in page objects: Your page object should only contain methods for interacting with the UI, not assertions or test-specific logic.

  • Not separating base actions: Common actions, like clicking a button or typing text, should be abstracted into a base class for easier reuse.

The main difference lies in how the elements are initialized:

  • Classic POM: You initialize the elements explicitly in the constructor or method (using driver.findElement()).

  • PageFactory: You use annotations (@FindBy) to initialize elements automatically when the page object is created.

While PageFactory can be convenient, it can lead to issues with lazy loading and dynamic elements. In contrast, the classic POM approach gives you more control over when and how elements are loaded.

In such cases, you can break your page object into smaller objects representing these sections. For example, instead of having one huge HomePage class, you could have separate classes like Header, Footer, and Sidebar that are used in the HomePage class. This makes your framework more modular and easier to maintain.

To improve performance:

  • Minimize element searches: Avoid searching for elements multiple times. Cache them in the page object class if needed.

  • Use waits efficiently: Use explicit waits where possible, rather than relying on Thread.sleep().

  • Reuse page objects: Instead of creating new instances of page objects repeatedly, use a singleton pattern to initialize them once and reuse across tests.

Yes, the Page Object Model is compatible with both JUnit and TestNG. It is a design pattern, not tied to a specific testing framework, so you can use it with any testing framework you prefer. The only requirement is that your test framework supports Selenium WebDriver.

💬 Comment Below If You’ve Seen Worse!

Look, I’ve seen it all — from page objects that were longer than my tax returns to tests that failed if you blinked wrong.

You don’t need to be perfect. You need to be clean. Predictable. Scalable.

POM is like that one tool in your QA belt that just works when done right.

So yeah — build it right the first time. Or refactor before it becomes a monster.

Also Don’t forget to check our blogs.

Table of Contents

One Response

Leave a Reply

Your email address will not be published. Required fields are marked *

Copyright © 2025 ScriptNG

Designed by ScriptNG