Every team eventually argues about the same thing: too many end-to-end tests and CI takes twenty minutes and flakes; too few and a broken checkout ships on a Friday. The old testing pyramid gave a rule of thumb, but it was drawn when writing an E2E test cost an afternoon — and that cost is changing. Here's how to think about the split in 2026.
What the pyramid still gets right
The classic shape — many unit tests, fewer integration tests, a thin cap of end-to-end tests — encodes one durable truth: the cost of a test and the cost of its failure both go up as you climb. A unit test runs in a millisecond, fails for exactly one reason, and points at one function. An E2E test boots a browser, walks a real user journey, and when it goes red you still have to figure out whether the bug is in your code, the test, the environment, or a third-party script that timed out.
That gradient hasn't gone away. What has changed is the authoring cost at the top of the pyramid, and that changes where the sweet spot sits. More on that below.
What E2E is uniquely good at
An end-to-end test is the only kind that answers the question your users actually ask: can I do the thing? It exercises the real stack — routing, auth, data layer, rendered DOM, network — in one pass. That makes it uniquely good at catching a specific class of bug that unit and integration tests structurally cannot:
- Wiring failures. Every unit passes, but the button's
onClickwas never bound, or the API route returns 200 with an empty body. Nothing below E2E sees this. - Contract drift between layers. The frontend expects
total_cents; the backend started sendingtotalCentslast sprint. Mocks hide this by definition, because you wrote the mock to match your assumption. - The critical path. Sign up, log in, add to cart, pay. If these break, nothing else matters. A handful of E2E tests over your revenue-generating flows are worth more than a thousand tests on a date formatter.
A single E2E test that covers checkout:
import { test, expect } from '@playwright/test';
test('customer can check out with a saved card', async ({ page }) => {
await page.goto('/cart');
await page.getByRole('button', { name: 'Checkout' }).click();
await page.getByLabel('Saved card ending 4242').check();
await page.getByRole('button', { name: 'Place order' }).click();
await expect(page.getByText('Order confirmed')).toBeVisible();
});
That one test crosses more system boundaries than a whole folder of units. That's the point — and also why you don't want a hundred of them.
Where unit tests win
E2E is a blunt instrument for logic. The moment a behavior has more than two or three meaningful branches, testing it through the UI is slow, fragile, and a poor description of intent. Business rules — pricing, permissions, validation, state machines, anything with edge cases — belong in unit tests, close to the code, where a failure names the exact input that broke.
import { describe, it, expect } from 'vitest';
import { applyDiscount } from './pricing';
describe('applyDiscount', () => {
it('never lets a percentage discount push the total below zero', () => {
expect(applyDiscount(50, { type: 'percent', value: 200 })).toBe(0);
});
});
Proving that rule through a browser would mean constructing a cart, applying a coupon, and reading a rendered total — for one of a dozen cases. As a unit test it's three lines and runs instantly. The rule of thumb: if you can express the case as an input and an expected output, it's a unit test. Save E2E for "does the journey hold together."
A sane ratio
Ratios are heuristics, not laws, and the honest answer is "it depends on your blast radius." But teams tend to over-index on one extreme, so a starting point helps:
| Layer | Rough share | What it covers |
|---|---|---|
| Unit | ~70% | Logic, branches, edge cases, pure functions |
| Integration | ~20% | Module seams, real DB/queries, API contracts |
| End-to-end | ~10% | Critical user journeys, cross-layer wiring |
The number that matters isn't the percentage — it's the absolute count of E2E tests. Most products have somewhere between five and thirty journeys that actually matter. Enumerate them: the flows that, if broken, would generate support tickets or lose money. That list is your E2E suite. Resist the urge to E2E-test the sixth variation of a settings toggle; that's a unit or integration concern wearing a browser costume.
Two anti-patterns to watch for:
- The ice cream cone — a fat layer of slow UI tests over a thin base of units. CI is slow, flaky, and everyone learns to re-run red builds instead of trusting them.
- The hollow middle — lots of units, a few E2E, nothing in between. Contract drift slips through the gap because no test exercises two real modules together.
Why generation changes the cost side
Here's the part the original pyramid couldn't account for. The pyramid is fundamentally an economic argument: E2E sits at the top because each one is expensive to write and maintain. Lower that cost and the optimal shape shifts — not toward an ice cream cone, but toward covering journeys you previously skipped because the authoring budget ran out.
That's the shift worth planning for. When a real end-to-end test for a journey can be produced from a plain-language description instead of an afternoon of getByRole archaeology, the marginal journey becomes worth covering. The five-flow suite grows to the fifteen flows that genuinely matter, without the maintenance tax that used to make teams cap it at five.
This is where a tool like TestVibe fits the strategy rather than replacing it. You describe a behavior in natural language; it generates the Playwright test by working through your live app in an isolated cloud session, and you run that test to confirm it does what you meant. The generated output is ordinary Playwright — readable locators, real assertions — so it lives in the same suite, under the same review, as anything you'd hand-write. Generation lowers the cost of the top of the pyramid; it doesn't change what belongs there.
The takeaway
The pyramid isn't obsolete — its economics just moved. Keep logic in fast unit tests where a failure names the cause. Reserve end-to-end tests for the journeys that lose you customers, and count them deliberately rather than letting them sprawl. Then, because authoring an E2E test no longer costs an afternoon, cover the handful of important flows you used to leave uncovered.
Want to put a real journey under test in minutes instead of an afternoon? Get early access, or read how AI generation works first.