Ch. 22 · Software Testing

Scoping End-to-End Tests

Reserve end-to-end tests for critical user journeys, use stable selectors, and avoid duplicating coverage the lower tests already have.

~2 min readadvancedupdated Oct 5, 2026

An end-to-end test drives the whole system through the browser or a real API, which gives the highest confidence and the slowest, most fragile feedback. Because each one is expensive, they should cover the few journeys that matter most, not every variation.

Before you start

You should be comfortable with test levels and selectors. This article covers what end-to-end tests should and should not cover.

Step-by-step walkthrough

Step 1: Cover critical journeys only

Choose the paths that must work for the product to function: sign in, checkout, the primary workflow. A handful of these catch integration failures across the stack. Everything else is cheaper to cover at a lower level.

Step 2: Use stable, accessible selectors

Select by role and accessible name rather than CSS classes or DOM paths, which change with styling and refactors. A selector tied to data-testid or an accessible role survives layout changes and doubles as an accessibility signal.

Step 3: Do not duplicate lower coverage

Every branch, boundary and error message does not belong in an end-to-end test, because that duplicates unit and integration coverage at far higher cost. Keep end-to-end tests to the integration of the journey, and push detailed cases down.

Worked scenario

A stable selector targets the button by its role.

await page.getByRole('button', { name: 'Place order' }).click();
await expect(page.getByRole('heading', { name: 'Order confirmed' })).toBeVisible();
JavaScript

Walk through the example

The test drives the checkout journey and asserts the confirmation, without touching every validation rule. Selecting by role and name keeps it stable across styling changes and reflects what a user sees. The detailed rules are covered by faster tests.

Common mistake

Writing hundreds of end-to-end tests that duplicate unit coverage, so the suite is slow and flaky and a failure does not indicate real risk. Another is selecting by brittle CSS classes or XPaths, so the tests break on unrelated styling changes.

Verify the behavior

Measure end-to-end suite duration and reduce it by moving duplicated cases down. Refactor a component’s markup and confirm role-based selectors still pass. Introduce a real integration bug and confirm a journey test catches it.

Interview exercise

Why should end-to-end tests be few?

Answer and reasoning

Because they are slow, run the whole stack and are more prone to flakiness and environmental issues, so each one carries high cost and lower reliability. Their value is confidence in the integration between layers, which a small number of critical journeys provides. Detailed variations are cheaper and more stable at the unit and integration levels.

Continue learning

Compare the levels in Integration vs unit and flakiness in Flaky test diagnosis. Read the Martin Fowler on the test pyramid and try the Testing interview questions.

More in Software Testing

read ✓Software Testing · hard

Testing Time with an Injected Clock

Inject a clock instead of calling the system time, freeze it in tests, and remove the sleeps that make tests flaky.

~2 min readread →
read ✓Software Testing · mid

Code Coverage and Its Limits

Read line and branch coverage as a signal, not a goal, and avoid the tests that chase the number without checking behavior.

~2 min readread →
read ✓Software Testing · mid

Testing Error Paths

Test failures and edge inputs deliberately, assert the error type and message, and stop the happy path from hiding bugs.

~2 min readread →
esc