All topics
Testingadvanced

End-to-End Testing with Playwright/Cypress

Automated browser tests that simulate real user interactions through an actual, full application stack, providing the highest-confidence but slowest and most expensive layer of the testing pyramid.

An end-to-end test drives a real browser through the real application.

End-to-end testing is like a full dress rehearsal of an entire theatrical production with the real cast, real costumes, real lighting, and a real live audience seated in the actual theater, versus a unit test which is more like one actor privately rehearsing a single line in front of a mirror — the dress rehearsal catches problems (a costume malfunction, a lighting cue firing at the wrong real moment) that rehearsing individual lines alone could never reveal, but you obviously can't afford to run a full dress rehearsal for every single line in the entire show.

Key Concepts

1
    test('a user can check out', async ({ page }) => {
      await page.goto('/products');
      await page.getByRole('button', { name: 'Add to cart' }).click();
      await page.getByRole('link', { name: 'Checkout' }).click();
      await page.getByLabel('Card number').fill('4242424242424242');
      await page.getByRole('button', { name: 'Pay' }).click();
      await expect(page.getByText('Order confirmed')).toBeVisible();
    });
2
It is the only layer that proves the whole thing works — the front end, the API, the database, the build, the deployment.
3
And it is the slowest and most fragile. A suite of five hundred end-to-end tests takes an hour, flakes regularly, and fails in ways that do not say what broke.
4
Which is why they belong at the top of the pyramid: a few critical journeys, not exhaustive coverage.
5
Selecting elements is where they become brittle.
    page.locator('.btn-primary')            breaks on a CSS change
    page.locator('#submit-btn-2')           breaks on a refactor
    page.getByRole('button', { name: 'Pay' })   stable, and accessible
    page.getByTestId('submit')              explicit, stable
6
Role and label selectors are preferred because they describe what the user sees — and a test that fails because the accessible name changed is telling you something real.
7
Flakiness comes from waiting.
    await page.waitForTimeout(2000);        // arbitrary -- flaky
    await expect(el).toBeVisible();         // retries until true
8
Auto-waiting assertions are the fix. A fixed delay is too short on a loaded CI machine and wasted time everywhere else.
9
Other sources. Shared state between tests, real network calls, animations, and time-dependent data. Each test should create its own data and be runnable alone.
10
Playwright against Cypress, briefly: Playwright runs out of process, supports several browsers properly, handles multiple tabs and is generally faster. Cypress runs in the browser, which makes debugging pleasant and imposes limits. Both are reasonable; Playwright is the more common current choice.
11
What to cover. Sign-up, sign-in, the primary transaction, and payment. The journeys where a failure is an incident.
12
Everything else belongs in a faster layer, because the value of an end-to-end test is confidence in the whole system, and that is worth paying for only on the paths that matter most.
13
What the interviewer is probing.1. "What is the main source of flaky E2E tests?" Probing: waiting. Stalls: "The network." Moves up: fixed sleeps — an auto-waiting assertion that retries until the condition holds is what removes the race.
14
2. "How should you select elements?" Probing: stability. Stalls: "By CSS class." Moves up: by role or an explicit test id; a class is a styling detail that changes without notice.
15
3. "How many E2E tests should you have?" Probing: proportion. Stalls: "As many as possible." Moves up: a handful covering the journeys where a failure is an incident — sign-in, the primary transaction, payment.
16
4. "Why must each test create its own data?" Probing: isolation. Stalls: "Shared fixtures are faster." Moves up: shared data makes tests interfere and order-dependent, which is the hardest kind of flake to diagnose.