Test What Users Actually Do: Frontend E2E Testing in Practice
E2E tests replay critical user journeys in a real browser, catching breaks between pieces that unit and integration tests don’t cover alone.
One-line takeaway: **E2E (End-to-End) tests replay critical user journeys in a real browser, catching breaks between pieces that unit and integration tests don’t cover alone.**
User experience is a connected flow—click → navigate → input → API → UI update. Testing only isolated pieces often misses failures in those connections. The key is validating the journey **as users experience it**. ([Playwright Docs](https://playwright.dev/), [Cypress Docs](https://docs.cypress.io/))
Background / Problem
Frontend defects often happen at boundaries:
- unexpected redirects after routing changes
- broken loading/error UX around API calls
- session/cookie issues leading to blank screens
Unit tests verify parts, but they don’t guarantee the full browser journey.
Core Concepts
E2E tests validate the flow
E2E tests run your app in a real browser and simulate what users do: click, type, navigate, and observe UI updates after network requests.
```mermaid
flowchart TB
U["Unit<br/>Function / Component"] --> I["Integration<br/>Module boundaries"]
I --> E["E2E<br/>Full user journey"]
U -->|"Fast feedback"| Goal1["Logic correctness"]
I -->|"Contract checks"| Goal2["Integration stability"]
E -->|"Real browser"| Goal3["User experience validation"]
```
Expected result: You can reason about failures by scope, making test design goal-driven.
Why unit tests alone miss real-world breakages
Unit tests can prove “a login function returns success.” Users experience “click login → navigate → session persists → page renders correctly.”
E2E tests target these connections and help catch failures users would feel immediately after release.
Approach
Approach 1) Keep E2E focused on critical journeys
Because E2E has runtime and environment cost, it’s usually most effective to cover the flows that hurt most when broken:
- auth and protected routes
- conversion flows (purchase/checkout/booking)
- search/filter → detail → key action
Expected result: You prevent high-impact regressions without inflating the suite.
Approach 2) Choose Cypress vs Playwright by how you operate
- Cypress: strong interactive debugging workflow. ([Cypress E2E Docs](https://docs.cypress.io/app/end-to-end-testing/writing-your-first-end-to-end-test))
- Playwright: test-runner oriented automation and browser coverage; Next.js has an official guide as well. ([Next.js + Playwright](https://nextjs.org/docs/pages/guides/testing/playwright))
Expected result: Tool choice becomes an operating decision, not a trend.
Implementation (Code)
The examples below keep the setup reproducible in a Next.js project, aligned with the official guide. ([Next.js + Playwright](https://nextjs.org/docs/pages/guides/testing/playwright))
1) Configure Playwright (start Next.js and run tests)
```ts
// playwright.config.ts
import { defineConfig } from "@playwright/test";
export default defineConfig({
use: { baseURL: "http://localhost:3000" },
webServer: {
command: "npm run dev",
url: "http://localhost:3000",
reuseExistingServer: !process.env.CI,
},
});
```
Expected result: Server startup and readiness are standardized across local and CI runs.
2) Write a scenario around user actions
```ts
// tests/navigation.spec.ts
import { test, expect } from "@playwright/test";
test("navigation works", async ({ page }) => {
await page.goto("/");
await page.getByRole("link", { name: "About" }).click();
await expect(page).toHaveURL(/\/about$/);
await expect(page.locator("h1")).toContainText("About");
});
```
Expected result: You validate the journey exactly as a user performs it.
Verification Checklist
Common Mistakes / FAQ
Q1. Isn’t unit testing enough for stability?
Unit tests give fast confidence for individual parts. User experience is the result of many parts working together.
E2E tests focus on those connections, reducing failures that users would notice immediately after release.
Q2. Why do E2E suites get flaky?
Shared state, time-based waits, and environment dependencies (data/network) are common sources.
Independent tests and condition-based checks improve reliability. ([Cypress Best Practices](https://docs.cypress.io/app/core-concepts/best-practices))
Q3. Should we apply E2E only to important pages?
Yes. E2E is most effective when it protects the journeys that matter.
Cover the rest with unit/integration tests to keep the overall suite balanced.
Summary (3–5 lines)
E2E tests replay user actions in a real browser and validate the end-to-end flow.
They’re especially good at catching issues around routing, sessions, and integrations that unit tests can miss.
Choose Cypress or Playwright based on how your team debugs, runs CI, and supports browsers.
Conclusion
E2E is not about maximizing test count. It’s about protecting journeys that matter end-to-end.
Start small with critical flows and keep the suite diagnosable so maintenance stays predictable.
References
- [Next.js + Playwright](https://nextjs.org/docs/pages/guides/testing/playwright)
- [Playwright Docs](https://playwright.dev/)
- [Cypress E2E Docs](https://docs.cypress.io/app/end-to-end-testing/writing-your-first-end-to-end-test)
- [Cypress Best Practices](https://docs.cypress.io/app/core-concepts/best-practices)