Skip to content

operationalwrocław--:-- cet

RITE NRG

∕ insights / page-object-model.md

Page Object Model

Page Object Model: A Practical Guide for Faster Releases

The page object model keeps UI tests cheaper to repair after UI changes. Learn the layer split, a login example, CI checks, and review gates.

>_article.meta

published
2026-10-29
reading_time
13 min read
topics
page object model, UI test automation, Selenium, test maintainability, CI pipelines

#01/article

Page Object Model: A Practical Guide for Faster Releases

A page object is a class for a page or component that hides locators and clicks behind business actions so tests describe intent. When a shared control changes, the fix lands in one place instead of scattering across every script that touched it. Repairs get shorter and diff reviews get smaller, which is what keeps a release window honest on the night a checkout label changes and the window does not.

Treat this as a release-capacity decision, not a style preference. Decide who owns the page layer and who owns the test layer, measure your current repair time as a baseline, and put a human review gate on any locator a generator produces. The suite either protects the window or becomes the reason it slips.

When Broken UI Tests Hold the Release

Say the product team renamed the checkout button after the last staging cut. By morning, dozens of UI scripts fail on the same control. The engineers who can repair them are already on the deploy bridge. The choice is ugly: mute the check and ship, or slip the window and lose the feature that was supposed to land today.

The cost is not a red build. It is slower time to market, hours spent hunting the same selector, feature capacity spent on repair instead of the next story, and higher release risk once flaky checks get silenced. A muted check does not reduce defects. It hides them until a customer finds them.

Deployment frequency, change lead time, change failure rate, and deployment rework rate are the scoreboard. DORA defines deployment frequency as how often changes are deployed, change lead time as the time from code commit to successful production deployment, change failure rate as the percentage of deployments causing production failures, and deployment rework rate as the percentage of deployments consisting of unplanned work to fix problems. A page object suite earns its place when regression feedback stays reliable without stretching the time to release or the unplanned work after it.

Those four numbers also depend on architecture, review practice, environments, product decisions, and operational readiness. A faster lead time after a cleanup is not proof the pattern did all the work. A rising change-failure rate or rework rate is still an early warning that the suite, the release path, or the UI needs attention before capacity and customer trust drop. Teams that treat quality as a delivery constraint, not a late gate, already track this kind of signal in software quality assurance.

Practical rule: Baseline lead time, deployment frequency, change failure rate, and rework rate before you consolidate duplicated UI steps, then compare after. Give the pattern only the credit the comparison can support.

Copy the ownership split next. If a class does not know what it owns, the rename still fans out across every script.

What Each Page Object Layer Owns

A diagram illustrating the three layers of the Page Object Model: Test, Page Object, and Shared Component.

Audit a suite against this table before you add another test. Selenium's page object guidance treats a page object as an interface to a page, or a meaningful component, that encapsulates locators and user actions while the test expresses business behavior. The application does not change. Coupling between scripts and markup does.

Layer Owns Must not own Example
Test Business scenario and outcome assertions Locators, waits, click sequences Sign in and expect the order id
Page object Private locators and domain actions, plus a loaded-screen check Business assertions and public element fields login() and submitOrder()
Component object Shared widget behavior Full-page journeys Header, modal, data table
Fixture or base Driver lifecycle, base URL, shared auth state Page-specific selectors Browser start and stored session

Test, page, and widget

The test owns the scenario and the assertions about outcomes. It calls login() and then expects an order id. It does not store selectors, sleeps, or click sequences. If a test file contains a CSS string, the boundary is already broken.

The page object owns private locators and methods named for work the user does, such as login() or submitOrder(), plus a narrow check that the expected screen or required controls are present. Selenium's documentation says page objects should generally avoid assertions, except to verify that the expected page or component has loaded. It must not assert business success, and it must not hand raw driver calls or public element fields back to the test.

A component object owns a repeated widget: header, dialog, data table, or address form. Pages compose those components. They do not inherit a deep page family that knows every menu. A fixture or base setup owns browser lifecycle, the environment URL, and shared authentication state. Page-specific selectors do not belong there.

Think of the page object as the shop counter and the test as the customer order. Selectors stay in the stockroom. When a label or DOM attribute changes, the repair stays at the counter. The same boundaries fit WebDriver, mobile UI automation, and later Playwright-style runners. The driver changes. The test intent does not.

Mark any public element field, or any business assertion inside a page class, as a boundary break before you write new tests.

The short walkthrough below shows the same split as a login screen rather than a diagram.

Maintenance Results and the Baseline to Build

Leotta and colleagues compared two equivalent Selenium WebDriver suites, one with page objects and one without, after the application under test went through a maintenance or evolution activity. In that industrial case, the page-object version required approximately three times less time to repair and about eight times fewer modified lines.

A comparison chart showing how Selenium Webdriver suites with and without page objects impact test maintenance effort.

Treat it as one industrial case, not a promise for every product. Do not carry the percentages straight into a business case. What transfers is the shape of the result: repair work shrank because selectors and interaction paths lived in one place, and that mechanism is something you can replicate and measure on your own suite.

Why the repair shrank

The mechanism is localization. One selector and one interaction path live in one class, so a shared header change is an edit in the header component rather than a hunt through every script. The risk of fixing one copy and missing another drops with it. SaaS teams with interfaces that move every sprint feel this first, because the regression suite has to move with the product, and release capacity is what the suite is really protecting.

Measures you can capture this week

Borrow the idea of a paired comparison, not the percentages. Assign one owner to the page layer and one to the test layer, then capture the signals below before you migrate and again after duplicated interactions are consolidated. Do not credit a shorter lead time to the pattern if the same sprint also changed review practice, environments, or architecture. Work like this belongs next to the rest of your engineering efficiency measures, not in a private spreadsheet.

Measure What it shows How to capture it
Locator churn per release Selector edits needed each release Count changed locators in version control diffs
Mean time to repair a failed UI test Effort to get a broken journey green again Failure-to-fix timestamps from CI and tickets
Duplicated selectors Same selector repeated across files Code search for each locator string
Flaky-test rate Failures with no product change behind them Reruns and quarantine history in CI
Deployment frequency How often changes reach production Release log
Change lead time Time from commit to successful production deployment Commit and deploy timestamps
Change failure rate Share of deployments that cause production failures Incident records linked to deployments
Deployment rework rate Share of deployments needing unplanned fixes Hotfix and rollback records

A rising change-failure rate or rework rate means the suite, the release path, or the UI design needs intervention. Waiting for a missed window makes the repair more expensive.

How to Implement a Login and Order Flow

Walk one revenue journey, sign-in then submit an order, and keep the classes small enough to sketch on one page.

A flowchart showing the five steps to implement a login and order flow using page objects.

  1. Map screens and shared widgets before any selector. You need a SignIn page, an Orders page, and a header component. If the header appears on both, it is not a method copied into both classes.
  2. Put locators in private fields. Prefer a dedicated test id, or an accessible role, over a CSS path that mirrors today's layout. A class name generated by the frontend build will rot on the next compile.
  3. Name methods for the business action. login(email, password) fills the form. submitOrder() uses the cart the test already built. The test should not assemble click sequences.
  4. Keep the wait inside the method that needs it. Every caller then shares one synchronization policy. A sleep in the test and a poll in the page is how flakes get two owners.
  5. On a single-page app, the page object confirms the route and that required controls are present. The assertion that an order id exists stays in the test. Loaded is not the same as correct.
  6. Compose the header into both pages. Do not inherit a base page that knows every menu, auth state, and product screen. Composition keeps the stack trace on the widget that failed.
  7. Write the test as intent. Open sign-in, log in, submit the order, then assert the confirmation. Three calls and one outcome check should be enough to read the scenario aloud.

An iframe or a new tab is a locator and driver concern inside the page or component, not a new assertion style. A shadow root or a mobile WebView changes how you reach the node. It does not change who owns the action.

Tomorrow's button rename edits SignIn once. The order test stays untouched. That is the failure this structure is meant to prevent.

class LoginPage {
  constructor(private driver: WebDriver) {}
  private email = By.css('[data-testid="email"]');
  private password = By.css('[data-testid="password"]');

  async login(email: string, password: string) {
    await this.driver.findElement(this.email).sendKeys(email);
    await this.driver.findElement(this.password).sendKeys(password);
    await this.driver.findElement(By.css('[data-testid="sign-in"]')).click();
    await this.driver.wait(until.urlContains("/dashboard"), 5000);
  }
}

// test
await signIn.login("ada@example.com", "correct-horse");
await orders.submitOrder();
expect(await orders.confirmationId()).toMatch(/^ORD-/);

The class hides the fields. The test never sees them.

Warning Signs to Reject in Review

A suite can use page object names and still couple every test to the DOM. Class names are not the pattern. The review has to reject the shapes that recreate the coupling.

A table outlining four common warning signs of bad page object model usage and their corresponding fixes.

Six breaks and the fix

Classes split so fine that one button gets its own class. Navigation becomes a maze, and a small UI change touches five files. Group classes by user-facing work, not by every control.

Methods return raw elements or expose the driver. The test then clicks whatever it wants, and the page class is a locator dump. Return a domain result, the next page, or nothing.

A base page knows headers, tables, auth, and every product screen. Stack traces stop naming the widget that failed, and every squad inherits someone else's menu. Compose component objects instead of growing the parent.

Business assertions sit inside the page class. When the test fails, the scenario is hidden behind a helper named verify(). Move outcome checks into the test. Leave only the loaded-screen check on the page.

Waits are scattered through tests. One screen sleeps for five seconds and another polls a spinner, so flakes have no single policy. Keep synchronization inside the interaction method that needs it.

Selectors use generated CSS classes. The next frontend build renames them and the suite goes red with no product change. Agree test ids or accessible roles with the frontend team, and reject layout-shaped paths in review.

If the pull request adds a public element field or a new inherited base page, send it back. Those two changes undo the split faster than a missing comment.

When several squads share a design system, component objects belong to the people who own that system. Squads add journey tests. They do not fork the header selector into a local copy that drifts by Friday.

Review Gates for AI and the CI Pipeline

Generated locators are not a free win. A 2025 global survey of more than 2,100 development and QA professionals found that 60 percent used AI somewhere in QA, while 92 percent struggled with changing requirements, 87 percent reported unstable environments, 80 percent lacked in-house AI-testing expertise, and only 33 percent maintained detailed test documentation. Another 2025 survey of more than 600 developers reported that three-quarters of teams using traditional code-based automation had adopted AI testing tools, yet some spent slightly more time maintaining suites after adoption.

Speed without a record is how a brittle selector, a changed assertion, or a logged secret lands in main. An agent can draft a page class, suggest a test id, convert an old script, or write the doc comment. It can also lock in a CSS path, change what the test claims to prove, expose sensitive application data, or self-heal over a real product regression.

A path with a named owner

Architects publish the allowed selector types and the layer boundaries. The agent proposes a diff. Existing tests still assert behavior. A named engineer reviews the diff. Rollback is a normal revert, not a special process. Record what was generated, which tests ran, which environment and data were used, and who approved production readiness. NIST's AI Risk Management Framework ties trustworthy AI to accountability and transparency, and it expects test sets, metrics, and tools used in evaluation to be documented. That record is the point. A green run with no author is not a release decision.

In the pipeline, run a short smoke of the changed journey on the pull request. Run the broader regression on a schedule or on the release candidate. Quarantine flakes instead of rerunning them until the build looks green. Fail the check when a new public locator appears, or when a disallowed selector type shows up.

The standard does not relax because an agent typed the class. Security, test validity, and production quality stay with named engineers. Tooling choices for that gate sit with the rest of your software delivery tools, under the same review, not beside it.

Decide in One Week Whether to Scale the Suite

Use the published repair-time and line-count results only to justify a pilot. Replace them with your own numbers before you scale.

  1. Pick one revenue journey, usually sign-in or checkout.
  2. Count duplicated selectors and note last release's repair time for that journey.
  3. Rebuild it with the ownership split and run it as the pull-request smoke.
  4. After two releases, compare repair time, flake rate, and the four delivery measures against the baseline. Log what else changed so the pattern gets only its share of the credit.

Scale only if repair effort fell and the smoke stayed trustworthy. Pause and fix boundaries if the new classes expose elements, hide assertions, or need more maintenance than the scripts they replaced. Reporting that risk early is ownership of the whole result, tests included, not only the feature diff.

If a modernization or a fixed-scope release is blocked by the suite, Rite NRG can act as the accountable partner. Senior architects own architecture, security, testing, and production quality, while agentic AI drafts boilerplate under the same review gates. The next action is the baseline on one journey, not a rewrite of the whole suite.


Rite NRG helps teams modernize test architecture so releases stay predictable, with senior engineers accountable for security, testing, and production quality while agentic AI speeds the boilerplate. If UI repair is holding a release, start with one journey and a human review gate rather than a full rewrite. Visit Rite NRG to plan that pilot with a partner who owns the result.

/ about the author

Written by the RITE NRG editorial team — the architects, engineers and delivery leads who build and operate AI-era software for our clients.

Technology Talent & Teams

IT Partnership Models: How to Choose the Right One

A decision-maker's guide to IT partnership models: what each model actually transfers to the partner, what it costs beyond the rate card, and how to match the model to your situation.

More guidance: all insights articles

Get a Free 30-Minute Scoping Call

Tell us about the system or idea behind this article. We tell you honestly if it is a fit — and what a fixed-scope delivery could look like.