Planning Coverage Before Writing a Single Test
Before opening the Cypress editor, map out the application's critical user journeys: for a typical e-commerce app that means account signup/login, product search and filtering, adding items to a cart, applying a discount code, checkout with payment, and order confirmation/history. E2E tests are expensive to write and maintain relative to unit tests, so the goal isn't to cover every possible UI state but to guarantee the handful of flows that would be a business emergency if they silently broke in production. Ranking flows by revenue impact and failure blast radius (checkout breaking is catastrophic; a cosmetic filter sorting bug is not) tells you where to invest E2E coverage versus where a unit or integration test is sufficient.
Cricket analogy: A captain sets the field for the specific batter and match situation rather than trying to cover every blade of grass, just as a test suite targets the checkout flow's failure risk rather than every pixel of the UI.
Setting Up Fixtures and a Clean Environment per Test
A reliable E2E suite needs a way to reach a known application state before each test without depending on leftover data from a previous run. Common approaches include seeding a test database via a backend API endpoint (or CLI script) in a cy.task or beforeEach hook, using cy.fixture() to load static JSON payloads for cy.intercept mocks when you want to isolate the frontend from a real backend, and resetting or truncating relevant tables between test files. Mixing both strategies is normal: use real API-backed flows for the handful of true end-to-end smoke tests that must catch integration bugs, and stub network responses with fixtures for the wider set of tests focused purely on frontend behavior, since stubbed tests run faster and aren't affected by backend flakiness.
Cricket analogy: Groundstaff re-roll and re-mark the pitch to identical specifications before every match so no team gets an unfair residual advantage from the previous game, just as a clean test database prevents leftover state from skewing results.
// cypress/support/commands.js
Cypress.Commands.add('seedCart', (items) => {
cy.request('POST', '/api/test/seed-cart', { items });
});
// cypress/e2e/checkout.cy.js
describe('Checkout flow', () => {
beforeEach(() => {
cy.task('db:reset');
cy.login('shopper@example.com', 'password123');
cy.seedCart([{ sku: 'SKU-001', qty: 2 }]);
});
it('completes checkout with a valid discount code', () => {
cy.intercept('POST', '/api/checkout').as('checkout');
cy.visit('/cart');
cy.get('[data-cy=discount-input]').type('SAVE10{enter}');
cy.get('[data-cy=discount-applied]').should('contain', '10%');
cy.get('[data-cy=checkout-button]').click();
cy.get('[data-cy=card-number]').type('4242424242424242');
cy.get('[data-cy=place-order]').click();
cy.wait('@checkout').its('response.statusCode').should('eq', 201);
cy.get('[data-cy=order-confirmation]').should('be.visible');
});
});Testing Critical User Flows End to End
The checkout flow above illustrates the shape of a real E2E test: it logs in, seeds specific preconditions (a cart with known items), exercises the actual user interaction (typing a discount code, filling payment fields), and asserts on both the network layer (the checkout API returned 201) and the visible UI (the confirmation message appeared). Testing both layers matters because a UI that shows a fake success message despite a failed API call is a worse bug than an outright crash, since it would mislead real customers into thinking their order went through. For payment fields specifically, use well-known test card numbers provided by the payment provider's sandbox (such as Stripe's 4242 4242 4242 4242) rather than real card data, and never run E2E tests against a production payment environment.
Cricket analogy: A third umpire checks both the stump microphone audio and the ball-tracking visual before confirming an edge, not just one signal alone, similar to asserting on both the API response and the visible confirmation UI.
Never point Cypress E2E tests at a real production payment gateway or send real card numbers, even accidentally. Use the payment provider's official sandbox/test mode and its documented test card numbers, and keep the sandbox API keys in a separate Cypress environment configuration (cypress.env.json or CI secrets) that is never merged with production credentials.
Running the Suite in CI with Parallelization
As the suite grows, running every spec sequentially becomes the bottleneck in CI. Cypress supports parallelization out of the box when paired with the Cypress Cloud recording service (or a self-hosted alternative) using cypress run --record --parallel, which load-balances spec files dynamically across multiple CI machines based on prior run durations rather than a naive even split, so machines finish at roughly the same time. Combine this with tagging or grouping (--tag smoke, --tag full) so pull requests can run a fast smoke subset covering the top-priority flows on every commit, while the full regression suite runs on a schedule or before a release, keeping feedback fast without sacrificing depth of coverage.
Cricket analogy: A tournament schedules multiple matches simultaneously across different grounds rather than forcing every match to happen one after another on a single ground, similar to parallelizing Cypress specs across CI machines.
Cypress's dynamic load balancing for --parallel relies on Cypress Cloud recording run history to estimate each spec's duration; on the very first parallel run with no history, specs are distributed evenly, and balancing improves on subsequent runs as timing data accumulates.
- Prioritize E2E coverage by business impact: test checkout and other revenue-critical flows most rigorously.
- Seed known application state before each test via API calls or a cy.task db reset rather than relying on leftover data.
- Mix real API-backed smoke tests with cy.intercept-stubbed tests for speed and isolation on the wider suite.
- Assert on both the network layer (response status/body) and the visible UI to catch mismatched success states.
- Use payment provider sandbox test cards and dedicated test API keys, never production payment credentials.
- Parallelize with cypress run --record --parallel to keep CI feedback fast as the suite grows.
- Separate a fast smoke suite for every commit from a full regression suite run on a schedule or before release.
Practice what you learned
1. Why should E2E test coverage be prioritized by business impact rather than covering every UI state?
2. What is the purpose of resetting or seeding application state before each test?
3. Why is it important to assert on both the API response and the visible UI in a checkout test?
4. What should be used for payment fields in an E2E checkout test?
5. How does cypress run --record --parallel distribute specs across CI machines?
Was this page helpful?
You May Also Like
Cypress Best Practices
A practical guide to writing Cypress tests that are fast, resilient to UI changes, and free of the flakiness that erodes trust in a test suite.
Cypress Quick Reference
A condensed reference of the most-used Cypress commands, assertions, network mocking patterns, and configuration options for day-to-day test writing.
Cypress vs Selenium vs Playwright
A technical comparison of the three major browser automation tools, covering architecture, language support, speed, and when each is the right choice.