Verdict
Yes
Yes, end-to-end tests are worth it for the main user flows: write one browser test per flow, run them on every pull request, and test rules and edge cases with unit tests.
Why
- Blast radiususers
- Change frequencyregularly
- Detectabilitysame-day
- Reversibilitywith-effort
- Test costmoderate
Yes, write one end-to-end test per main user flow, and leave rules and edge cases to unit tests. My typical case is a web app with one frontend, backend and database, and flows such as signing in and inviting a teammate. Blast radius is users, because a broken flow stops customers from working, and Change frequency is regularly, because most monthly features touch a flow. Detectability is same-day, because a customer who cannot finish a flow writes to support within a day, and Reversibility is with-effort: a hotfix plus replies to tickets. Test cost is moderate once the app starts in CI with a seeded database, because a Playwright test of one flow takes me about an hour to write and changes with the flow, so rule R12 gives Test minimally; with Test cost heavy, the decision becomes Do not test.
| When | Decision | Why |
|---|---|---|
| The flow takes a card payment, such as checkout or a plan upgrade | Test mandatory: an end-to-end test of checkout in the payment provider's test mode, plus unit tests for every amount | Blast radius rises to money and Reversibility to costly, because a double charge needs a refund |
| The frontend and backend teams release separately, and a renamed field saves a form with an empty value and no error | Test: an end-to-end test for each form that saves data, which reloads the page and checks every saved field | Detectability moves to eventually, because the saved record looks complete |
| The flow crosses four services and an external sign-in provider, and a broken hand-off leaves the dashboard showing last week's numbers while every step returns success | Test it differently: a synthetic check that signs in to production as a test account every 10 minutes and alerts when the dashboard data is over six hours old | Test cost rises to heavy, because every service must run with seeded data, and Detectability moves to eventually, because stale numbers look like real ones |
| The end-to-end tests need a shared staging database, so runs fail on data that another team changed | Do not write end-to-end tests against staging; test each API with integration tests instead | Test cost rises to heavy, because the tests break for reasons outside the code, while Detectability stays same-day |
| The app is an internal admin tool whose screens change a few times a year | Do not write end-to-end tests; click through the changed screen after each release | Blast radius falls to internal and Change frequency to rarely |
What breaks if you don't test
Without an end-to-end test, nothing checks the seam between frontend and backend. The frontend is tested against a fake API and the backend with a client that skips what a browser does, so both suites stay green when the form sends a field or a header that the server rejects. Customers see an error after the release, and the first ticket arrives the next morning.
What you lose if you over-test
Browser tests for edge cases multiply run time: a Playwright test of one flow takes about eight seconds in my suites, so 400 of them run 53 minutes on one worker. Browser tests also depend on timing and test data, and after a few red runs that pass on retry, people retry by reflex and miss a real failure.
How to test
- Write one Playwright test per flow that customers cannot work without, through the real frontend, backend and a seeded database started by webServer.
- Reload the page and check the saved record, not only the confirmation message.
- Give each test its own data, as the Playwright guide recommends, and run the suite on every pull request.
- Put branches and boundaries in unit tests: a failing end-to-end test says little about the cause, as Just Say No to More End-to-End Tests shows.
When the answer changes
- The flow moves money or shows records that other customers must not see.
- Running the tests needs shared environments or other teams' services.
- The screens are internal and change a few times a year.
Real incident + Code example
The invite form that both test suites passed
On a B2B scheduling product I worked on, a frontend developer tested a new Invite teammate dialog with Vitest against Mock Service Worker handlers. A backend developer tested the Django view behind it with Django's test client, which relaxes CSRF checks by default. The dialog posted without the X-CSRFToken header, so Django rejected every invitation with a 403. Both suites were green when we released on a Thursday evening, and nine admins had written to support by the hotfix at Friday noon. The fix came with this test:
import { test, expect } from "@playwright/test";
test("an admin invites a teammate", async ({ page }) => {
await page.goto("/login");
await page.getByLabel("Email").fill("admin@acme.test");
await page.getByLabel("Password").fill("seeded-password");
await page.getByRole("button", { name: "Sign in" }).click();
await page.goto("/settings/team");
await page.getByRole("button", { name: "Invite teammate" }).click();
const dialog = page.getByRole("dialog");
await dialog.getByLabel("Email").fill("new.member@acme.test");
await dialog.getByRole("button", { name: "Send invitation" }).click();
await expect(page.getByText("Invitation sent")).toBeVisible();
await page.reload(); // the invitation must be saved, not only shown
await expect(page.getByRole("row", { name: /new\.member@acme\.test/ })).toBeVisible();
});
Related questions
FAQ
- Are end-to-end and integration tests worth it for non-critical code?
For code that customers use, one end-to-end test per main flow is worth it even for non-critical code, because a broken flow costs a day of tickets and a hotfix. For an internal tool whose screens change a few times a year, skip them and click through the changed screen after each release.
- How many end-to-end tests should I write?
Write one end-to-end test per flow that customers cannot work without; in the apps I worked on, that came to about ten tests. Add one each time a bug crosses the seam between frontend and backend.
- Are end-to-end tests too flaky to be worth it?
End-to-end tests that fail for reasons outside the code, such as a shared staging database, raise Test cost to heavy and no longer pay off. A test with its own seeded database and Playwright's waiting assertions instead of fixed delays stays stable enough for every pull request.