Flow-Based Coverage
Definitive Proof
A passing flow proves success in a way that test suites cannot.
Each flow is a complete user journey — start to finish — not isolated tests.
1 script per user journey
Not 8–12 fragments
Passing flow = proof the feature works
Not just that parts rendered
Failures pinpoint exactly which step broke
Not cascading noise
Flows Fix The Fragmented Test Problem
A green CI run is not the same thing as a working product. Fragmented test suites can pass completely while critical user journeys are broken.
Checkout is broken
An API contract change broke the state handoff between cart and checkout. Every slice still passes. The integration is silent.
“A suite of 400 tests can pass perfectly.
Checkout can still be broken.”
The unit of measurement doesn't match the unit of value. Counting tests is not the same as verifying journeys. Each isolated test checks a slice — none of them answer the only question that matters: does the feature work?
What a Flow Actually Is
A flow is defined by three things — not by the tool, the language, or the number of assertions. Answer these three questions for any feature and you have a flow.
What does the user want to accomplish?
This defines the flow's scope and name. e.g. “Complete a purchase as a guest.”
What is the starting state?
Where does the script begin? e.g. “On the product page, not logged in.”
What is the observable, measurable outcome?
The assertion that makes the result definitive. e.g. “The ‘Order Confirmed’ page appears with an order ID.”
The key rule: a flow cannot produce a false positive. If the script reaches the measurable outcome without failing any step, the feature works end to end, right now. No isolated check can make that claim.
test.describe('Guest Checkout Flow', () => {
test('completes checkout end to end', async ({ page }) => {
await test.step('Add product to cart', async () => {
await page.goto('/products/sneakers');
await page.getByRole('button', { name: 'Add to Cart' }).click();
await expect(page.getByTestId('cart-count')).toHaveText('1');
});
await test.step('Proceed through checkout', async () => {
await page.goto('/checkout');
await page.getByTestId('shipping-name').fill('Jane Doe');
await page.getByTestId('shipping-address').fill('123 Main St');
await page.getByRole('button', { name: 'Continue' }).click();
});
await test.step('Complete payment', async () => {
await page.getByTestId('card-number').fill('4242424242424242');
await page.getByRole('button', { name: 'Place Order' }).click();
});
await test.step('Verify order confirmed', async () => {
await expect(page.getByText('Order Confirmed')).toBeVisible();
await expect(page.getByTestId('order-id')).toBeVisible();
});
});
});What a Flow Gives You
A flow is a single script that runs a complete user journey — real browser, real API calls, real session — from entry to measurable outcome. Its result is unambiguous.
Flow passes
Checkout works. Ship with confidence.
Flow fails at step 3
Shipping selector broke. Exact location, instant diagnosis.
Definitive signal
If the flow passes, the feature works — end to end, right now, in the environment where it ran. No fragmented suite can make that statement.
Precise failure attribution
When a flow fails, the step name, screenshot, and trace point to one location. No cascading noise from 12 tests sharing the same root cause.
Minimal maintenance surface
One flow to maintain instead of twelve. When your UI changes, one script updates. Selector changes live in a single page object.
Catches integration bugs
State propagation failures, session edge cases, and API contract mismatches between steps only appear in realistic navigation sequences. Flows run them. Slices skip them.
Business-legible coverage
A list of flows — Login Flow, Checkout Flow, Subscription Upgrade Flow — answers the coverage question any stakeholder can ask. “340 tests passing” answers nothing. Flow-based coverage maps directly to the features that generate revenue, making QA conversations into product conversations.
Flows vs. Fragmented Tests
The difference isn't in the technology. It's in what the result actually tells you.
| Dimension | Fragmented Tests | Flow-Based Coverage |
|---|---|---|
| Does a passing result prove the feature works? | No — parts pass while the journey can be broken | Yes — a passing flow proves the journey works |
| Are integration bugs between steps caught? | No — each test mocks what it doesn’t own | Yes — the full sequence runs without mocking |
| Scripts per user journey | 8–12 fragments | 1 flow |
| Updates when a UI element changes | 8–12 updates across multiple files | 1 update in one place |
| Readable by non-engineers? | No — requires decoding test runner output | "Checkout Flow: passing" is self-explanatory |
| Can the suite accumulate false confidence? | Yes — grows toward entropy with every sprint | No — coverage is always journey-mapped |
Which Journeys Deserve a Flow?
Not all user journeys are equally important to automate. Classify by what breaks when they fail.
Revenue-critical
If this breaks, you stop earning.
- Checkout
- Subscription upgrade
- Payment method update
Retention-critical
If this breaks, users churn.
- Login
- Core product workflow
- Daily-use features
Growth-critical
If this breaks, acquisition slows.
- Sign-up
- Onboarding
- Email verification
Support-intensive
If this breaks, your queue fills.
- Password reset
- Invoice download
- Data export
How Many Flows Do You Need?
A 3-step framework for translating your app's user goals into a flow count.
List user goals, not features
Users come to your product to accomplish things, not to use features. Write down every primary user goal. “User runs a report” is a goal. “Report dropdown renders” is not.
Add variants for meaningful path differences
Each significant variant gets its own flow. Guest checkout and authenticated checkout are two different journeys, even though both end at “Order Confirmed.” Invalid-credentials login is a distinct variant worth its own flow.
Bundle simple variants into one script
If Admin login, Supervisor login, and User login all follow the same form and fit within a single script's scope, that's 1 flow — not 3. Bundling keeps your count lean without losing coverage.
Rule of thumb: most apps with 5–10 core user goals need 20–50 flows for meaningful journey coverage. Start with revenue-critical and retention-critical paths first, then expand outward.
example — SaaS product
| User Goal | Variants | Flows |
|---|---|---|
| Sign up | Email + Google | 2 |
| Login | Email / Google / MFAbundled | 1 |
| Connect data source | Standard flow | 1 |
| Run a report | Create + share | 2 |
| Invite a teammate | Admin role | 1 |
| Upgrade plan | Free → Pro | 1 |
| Password reset | Standard flow | 1 |
| Total | 9 |
7 user goals → 9 flows. Add edge cases and you'd reach ~15–20 for full confidence.
Common Questions About Flows
Definitions, comparisons, and clarifications on the flow model.
From the Blog
“Your Tests Are Passing. Your Feature Is Broken.”
A deeper look at why fragmented test suites produce false confidence — and the five concrete reasons flows are categorically different from any suite of isolated checks.
Read the ArticleSee what flow coverage looks like on a real product
We'll map your critical user journeys live and show you exactly what a flow covers that your current test suite misses.