QuestionsTesting

End-to-end testing: when and how

End-to-EndHardTesting

When are Playwright/Cypress-style e2e tests worth it, what are the tradeoffs, and how do you keep them stable?

What it tests

Judgment about the cost/confidence of e2e plus the concrete practices that keep a real-browser suite from rotting.

Approach & answer

E2E tests drive the fully assembled app in a REAL browser as a user would — navigate, click, type, assert on rendered UI — usually against a running server and often a real (test) backend. Their value is the HIGHEST confidence: they prove the whole system actually works together, catching integration gaps that unit/component tests can't (routing, auth flow, real DOM/CSS, the bundle actually loading). The tradeoffs are why they sit at the TOP of the pyramid: they're the SLOWEST (seconds to minutes each, real network/render), the most EXPENSIVE to write and maintain, and the most PRONE TO FLAKINESS (timing, environment, data setup). So the judgment is: use e2e for a SMALL number of CRITICAL user journeys — sign-up/login, checkout/payment, the one or two flows that would be catastrophic if broken — and push everything else down to faster layers. Don't re-test every field validation or edge case through the browser; do that in component/unit tests, and use e2e to prove the happy path and a couple of high-value error paths end to end. Keeping them stable is a discipline: (1) use AUTO-WAITING, resilient LOCATORS (Playwright's getByRole/getByLabel, web-first assertions that retry) instead of fixed sleeps or brittle CSS selectors; (2) control TEST DATA — seed a known state via API/DB fixtures and reset between runs, don't depend on data that drifts; (3) make tests INDEPENDENT and idempotent (each creates its own user/data, can run in any order and in parallel); (4) stub only truly external/third-party services (payment sandboxes, email) while keeping YOUR stack real; (5) run headless in CI with tracing/video/screenshots on failure so you can debug the intermittent ones; (6) shard across machines to keep wall-clock down. Modern tools (Playwright especially) reduce flakiness with auto-waiting and isolated browser contexts, but the strategic point stands: e2e is a scalpel for critical flows, not a bucket for coverage.

Use this technique when

Deciding what deserves an e2e test; stabilizing a slow/flaky browser suite.

Code

import { test, expect } from '@playwright/test';

test('user can log in and reach the dashboard', async ({ page }) => {
  await page.goto('/login');

  // Resilient, user-facing locators + auto-waiting web-first assertions
  await page.getByLabel('Email').fill('ada@example.com');
  await page.getByLabel('Password').fill('correct horse');
  await page.getByRole('button', { name: 'Sign in' }).click();

  // Retries until true; no fixed sleep
  await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
});
// Reserve e2e for critical journeys; seed data via API; keep each test independent.

References