End-to-End Testing
debt(d7/e7/b7/t7)
Closest to 'only careful code review or runtime testing' (d7). The detection_hints note automated=no, and the code_pattern is 'test suite dominated by E2E tests; slow flaky CI pipeline >30 minutes' — this is visible only through CI metrics, code review of the test pyramid shape, or when the team notices chronic flakiness and slowness. Tools like Playwright/Cypress run the tests but don't flag over-reliance on E2E; no static analysis catches a bad test pyramid ratio automatically.
Closest to 'cross-cutting refactor across the codebase' (e7). The quick_fix says to move logic to unit tests and restrict E2E to 5 critical journeys. Reversing a test suite dominated by E2E tests means rewriting many existing E2E scenarios as unit/integration tests, restructuring CI pipelines, and establishing new testing conventions across all teams — this is a cross-cutting effort touching many files, pipelines, and developer habits rather than a simple parameterised fix.
Closest to 'strong gravitational pull' (e7... b7). A test strategy dominated by E2E tests imposes a persistent productivity tax on every team member: slow CI blocks every PR, flaky tests require triage, and every new feature is expected to follow the same pattern. The applies_to scope is web broadly, and the common_mistakes confirm high maintenance burden and distrust cascading across the whole suite. Not quite b9 (doesn't rewrite the architecture), but every change is shaped by the slow feedback loop.
Closest to 'serious trap' (t7). The canonical misconception is explicitly stated: 'More E2E tests means better coverage.' This directly contradicts the test pyramid principle that most competent developers have learned in other testing contexts, where 'more tests = better' is usually correct. The trap is that E2E tests feel thorough but introduce fragility and slowness — a well-documented gotcha that developers repeatedly fall into, especially those coming from manual QA backgrounds or greenfield projects.
Also Known As
TL;DR
Explanation
E2E tests (Playwright, Cypress) drive a real browser: click buttons, fill forms, assert page content. They test the full stack — frontend, API, backend, database — in a realistic environment. The trade-off: slow (seconds per test), flaky (timing issues, external dependencies), and expensive to maintain. The test pyramid says E2E should be sparse — cover critical user journeys (checkout, signup, core workflow) and leave edge cases to unit and integration tests.
Diagram
flowchart LR
PLAYWRIGHT[Playwright or Cypress] --> BROWSER2[Real browser<br/>Chromium Firefox]
BROWSER2 --> CLICK[Click buttons fill forms<br/>assert page content]
CLICK --> STACK[Tests full stack<br/>UI API DB]
subgraph Best_Practices
WAIT[Wait for elements<br/>not arbitrary timeouts]
SELECTORS[Use data-testid selectors<br/>resilient to style changes]
SPARSE[Few E2E tests<br/>critical paths only]
PARALLEL[Run in parallel<br/>across browsers]
end
subgraph Failures
SCREENSHOT[Capture screenshot on fail]
VIDEO[Record video on fail]
end
style STACK fill:#6e40c9,color:#fff
style WAIT fill:#238636,color:#fff
style SCREENSHOT fill:#1f6feb,color:#fff
Common Misconception
Why It Matters
Common Mistakes
- Too many E2E tests — slow CI, high maintenance burden, flaky failures breed distrust of the whole suite.
- No wait strategies — hard-coded sleeps instead of waiting for elements; flaky tests.
- Running E2E against production — tests create real data, send real emails, charge real cards.
- Not capturing screenshots/videos on failure — debugging E2E failures without visual context is very slow.
Code Examples
// Flaky E2E — hardcoded sleep instead of waiting:
await page.click('#submit-btn');
await page.waitForTimeout(3000); // Hope 3s is enough
await expect(page.locator('#success-msg')).toBeVisible();
// Fails on slow CI, passes locally — nightmare to debug
// Playwright — wait for element, not arbitrary time:
await page.click('#submit-btn');
await expect(page.locator('#success-msg')).toBeVisible({ timeout: 10_000 });
// Waits up to 10s, passes as soon as element appears
// Structure: test critical paths only
test('user can complete checkout', async ({ page }) => {
await page.goto('/products');
await page.click('[data-testid="add-to-cart"]');
await page.click('[data-testid="checkout"]');
await page.fill('#card-number', '4242424242424242');
await page.click('#pay-now');
await expect(page.locator('.order-confirmation')).toBeVisible();
});