An end-to-end test is the only kind of test that exercises the entire real system the way an actual user does — which is exactly what makes it both the most valuable single test in a suite and the most expensive one to run and maintain.
3 min read
// Playwright — drives a REAL browser against a REAL running app
test("a user can sign up and land on the dashboard", async ({ page }) => {
await page.goto("https://staging.example.com/signup");
await page.fill('[name="email"]', "ada@example.com");
await page.fill('[name="password"]', "correct-horse-battery-staple");
await page.click('button:has-text("Sign up")');
await expect(page).toHaveURL(/\/dashboard/);
await expect(page.getByText("Welcome, ada@example.com")).toBeVisible();
});Every earlier lesson in this domain has involved some substitution — a stub, a fake, a real-but-disposable test database instead of production infrastructure. An end-to-end (E2E) test substitutes nothing: a real browser (Chromium, Firefox, or WebKit, actually rendering pixels and running real JavaScript) drives real UI interactions against a genuinely running instance of the full application — real frontend, real backend, real database, often a real (sandboxed) version of every third-party integration. This is the only kind of test in this entire domain that can catch a bug in how all the pieces are actually deployed and connected together, not just how they're wired in code.
A single E2E test can take seconds where a unit test takes milliseconds — booting (or reusing) real infrastructure, rendering a real page, waiting for real network round trips adds up fast, and a suite of hundreds of E2E tests can genuinely take longer to run than the rest of the entire test suite combined. They're also the most expensive to maintain: because they touch the real UI, a legitimate redesign (a button moves, a class name changes, a flow gets an extra confirmation step) can break dozens of E2E tests at once, even when no actual bug was introduced — a real, recurring maintenance cost that unit tests, testing behavior at a much smaller and more stable surface, mostly avoid. And when an E2E test fails, "something in this entire system, top to bottom, is wrong" is a far harder starting point to debug from than a focused unit test's specific failure.
Because E2E tests are the only layer that catches real deployment and integration issues, skipping them entirely leaves a genuine blind spot — code that passes every unit and integration test can still be broken by a misconfigured environment variable, a CDN serving stale JavaScript, or a real third-party API behaving differently than its sandbox. But because they're slow and expensive to maintain, writing E2E tests for every single behavior a unit test could just as well cover means paying that cost for no real additional protection. The practical answer both extremes miss: a small number of E2E tests covering the handful of flows where a real, deployed break would be genuinely catastrophic (signup, checkout, login) — not comprehensive coverage of every edge case, which is what the unit and integration layers underneath are for.
Check your understanding
A quick comprehension check — not tracked, not graded, just for you.
1. What makes an end-to-end (E2E) test different from every other kind of test in this domain?
2. What's the real cost of E2E tests that makes 'just write E2E tests for everything' a bad strategy?
3. What's the practical answer to how many E2E tests a suite should have?
Testing & QA