neart.ai
EcosystemStoryHow We BuildPricingBlog
Try Inspected →
neart.ai
EcosystemStoryHow We BuildBlog

Ní neart go cur le chéile

A SaltCore Group Limited company

© 2026 neart.ai · SaltCore Group Limited. All rights reserved.

Software Quality

How Do You Keep End-to-End Tests Maintainable as Your App Grows?

12 January 20264 min read

## The short answer


Keep end-to-end tests maintainable by separating *what* a test does from *how* it interacts with the UI. Abstract the details of locating and clicking elements behind reusable objects — commonly the Page Object Model — so that when the interface changes, you update one place rather than fifty tests. Combine that with shared setup helpers, stable selectors, and treating test code with the same care as production code. Without this discipline, an E2E suite becomes so costly to update that teams abandon it.


Maintainability is what determines whether an E2E suite survives its second year.


## Why E2E tests rot


E2E tests are tightly coupled to the user interface. Every time a button moves, a label changes, or a form is restructured, tests that reference those elements directly break. In a growing app, the UI changes constantly. If each change forces edits across dozens of brittle tests, the maintenance burden balloons until the suite is more trouble than it's worth.


The goal of every technique below is to localise the impact of change.


## The Page Object Model


The most widely used pattern. A page object is a class or module that represents a screen or component and exposes meaningful actions, hiding the underlying selectors.


Instead of a test that finds an input by its CSS selector, types an email, finds a button, and clicks it, you write something like `loginPage.signIn(email, password)`. The selectors live inside `loginPage`.


Benefits:


- **One place to update** when the UI changes — the page object, not every test.

- **Readable tests** that describe behaviour, not mechanics.

- **Reuse** — the same login action serves every test that needs it.


Keep page objects focused: they model interaction and expose actions, but the assertions usually stay in the tests so each test's intent is clear.


## Use stable, intention-revealing selectors


Tests break when they rely on fragile selectors — deep CSS paths, auto-generated class names, or text that changes with copy edits.


Better practices:


- **Add dedicated test attributes** (such as `data-testid`) to important elements, so tests target a stable hook independent of styling.

- **Avoid coupling to layout** — don't navigate by "third button in the second div".

- **Avoid coupling to display text** for anything that might be reworded or localised.


Stable selectors are one of the highest-leverage investments in E2E maintainability.


## Share setup and common flows


Many tests need the same precondition — a logged-in user, a seeded account, an item in the basket. Don't repeat that setup in every test.


- **Extract setup into helpers or fixtures** so it's defined once.

- **Use API-based setup where possible** — log in or create data through the API rather than clicking through the UI, which is faster and less brittle.

- **Reserve UI-driven setup** for when the journey itself is what you're testing.


This keeps tests short and focused on the behaviour they actually verify.


## Treat test code as production code


Test code deserves the same engineering standards as the application:


- **Review it** in pull requests.

- **Refactor it** when it gets repetitive or unclear.

- **Name things well** so a failing test reads like a sentence.

- **Avoid duplication** through shared utilities and page objects.


A suite written carelessly will decay; one written with care stays an asset.


## Keep tests independent and focused


Each test should verify one journey and stand alone:


- **One clear purpose per test**, so a failure points to a specific problem.

- **No dependence on other tests** having run first.

- **Self-contained data**, created and cleaned up by the test itself.


Independence makes tests easier to read, reorder, parallelise, and fix.


## Prune relentlessly


Maintainability isn't only about how you write tests — it's about how many you keep. Periodically:


- **Remove tests for journeys that no longer matter.**

- **Merge overlapping tests** that cover the same ground.

- **Push coverage down** to unit or integration level where an E2E test is overkill.


A smaller, sharper suite is cheaper to maintain and faster to run.


At neart.ai, building enterprise-grade products over time means our E2E suites have to age well — page objects, stable selectors, and disciplined pruning are what keep them an asset rather than a liability.


## A maintainability checklist


1. Are UI details abstracted behind page objects?

2. Do tests use stable, dedicated selectors rather than fragile ones?

3. Is common setup shared and done via API where possible?

4. Is test code reviewed and refactored like production code?

5. Is the suite pruned regularly to stay lean?


## Takeaway


Maintainable E2E tests come from localising the impact of change. Hide UI mechanics behind page objects, target stable selectors, share setup, treat test code as production code, and prune the suite relentlessly. Do this and your tests will keep paying off as the app grows instead of becoming the thing nobody dares to touch.

Related posts

Software Quality

How Do You Ship Software Without Regressions?

Software Quality

What Is End-to-End Testing, and When Should You Use It?

Software Quality

What Does WCAG 2.2 Mean for Your Testing Strategy?