Community content. Review instructions before giving them to an AI agent — treat modules like open-source code.
E2E Testing Rules
Writing end-to-end tests that stay reliable and fast: covering critical user journeys not everything, resilient role/test-id selectors, waiting on conditions instead of sleeping, test data isolation, treating flakiness as a bug, and keeping the suite runnable.
Mby @markdownersPublished August 21, 2026 · ~4 min read
0 downloads · Used by 0 stacks
End-to-end tests exist to catch the failures unit tests structurally cannot see — real browser rendering, real navigation, real integration between frontend and backend — and that value only holds if the suite stays fast and reliable enough that people actually run it and trust its failures.
Cover critical journeys, not everything
- Write e2e tests for the handful of journeys where a break would be a genuine incident — signup, login, checkout, the core create/publish flow — not for every UI state and permutation, which belongs in unit/component tests that run faster and fail with a clearer signal.
- Treat e2e coverage as a pyramid's thin top layer: broad logic coverage lives in unit tests, component-level interaction in component tests, and e2e verifies that the real integrated system serves the critical paths correctly end to end.
- Resist "let's e2e test this too" for anything a faster test type can already verify — every e2e test added is suite runtime and flakiness surface added permanently.
Resilient selectors
- Select elements by role and accessible name (
getByRole('button', { name: 'Submit' })) or by a dedicateddata-testidattribute — never by CSS class, DOM structure, or text content that's likely to change with a redesign or a copy edit unrelated to the behavior being tested. - Treat brittle selectors (nth-child, deep CSS descendant chains, auto-generated class names from CSS-in-JS) as a test smell to fix immediately, not something to patch around — they turn every unrelated markup change into a wave of test failures.
- Prefer role-based selectors over
data-testidwhere practical — they also verify the page is accessible, catching a real regression (missing label, wrong role) as a side effect.
No arbitrary sleeps
- Never use a fixed
sleep/wait(2000)to "give the page time to load" — wait on the actual condition (element visible, network request settled, specific text present) using the framework's built-in auto-waiting or explicit wait-for assertions. - A fixed sleep is either too short (flaky under load) or too long (slows the whole suite for no benefit) — waiting on a condition is both faster on the common case and correct on the slow one.
- When a wait condition is genuinely hard to express (an animation, a debounce), wait on the resulting state change (final position, element becoming stable/interactable) rather than an elapsed duration.
Test data isolation
- Give each test (or each test file, depending on framework parallelism) its own data — a fresh seeded account, a fresh record — rather than sharing mutable fixtures across tests, which creates order dependence: test B only passes because test A ran first and left state behind.
- Clean up data a test creates, or run against a database that's reset between suite runs — tests that accumulate state run-over-run eventually hit unrelated failures (duplicate-key errors, unexpectedly long lists) that have nothing to do with the behavior under test.
- Avoid tests that depend on execution order to pass — a suite must produce the same results whether run in full, in parallel, or as a single isolated test, otherwise "run just this one test to debug it" stops being a reliable technique.
Flakiness is a bug
- Treat any intermittently failing e2e test as a bug to root-cause and fix — never as something to retry until green, skip, or quarantine indefinitely; a suite with tolerated flakiness trains everyone to ignore red CI, which is worse than not having the test.
- When a flaky test is found, reproduce it locally (often by removing an implicit wait or slowing the network) before attempting a fix — a fix applied without reproducing the failure is usually a sleep added in the wrong place, which hides the symptom instead of curing it.
- If a genuine retry policy is used for infrastructure-level flakiness (network blips in CI), log and track retry counts — a test that needs frequent retries to pass is still signaling a real problem even if it eventually goes green.
Keeping the suite fast enough to run
- Run e2e tests in parallel across independent workers/browsers by default, since serial e2e execution scales linearly with suite size and quickly becomes too slow to run on every change.
- Keep the e2e suite separate from the fast unit/lint gate that runs on every save — run it on PRs/pre-merge or on a schedule, so its necessarily higher latency doesn't punish the fast local feedback loop.
- Periodically prune e2e tests that no longer cover a critical journey (a removed feature, a consolidated flow) — an e2e suite that only grows becomes slow enough that people stop running it locally, which defeats its purpose.
Badge
Link back to this module from your own README.
[](https://markdowners.com/m/markdowners/e2e-testing-rules)Discussions about this module
No discussions about this module yet.
Start a discussion
Comments (0)
Sign in to comment. Sign in
No comments yet. Be the first to add one.