The Problem
Your team writes tests inconsistently. Some modules have 90% coverage, others have none. API tests break every time the backend team deploys because they hit real endpoints. Browser tests are flaky because they depend on third-party services being available. When a regression slips through, nobody discovers it until a customer reports the bug three days later. The testing strategy is a patchwork of ad-hoc efforts rather than a systematic pipeline.
The Solution
Use test-generator to create comprehensive unit tests for untested modules, msw (Mock Service Worker) to intercept API calls with realistic mock responses so tests never depend on live services, playwright-testing for reliable browser E2E tests, and regression-tester to automatically detect visual and functional regressions before they reach production.
Step-by-Step Walkthrough
1. Generate unit tests for critical business logic
Start with the modules that handle money, authentication, and data integrity.
Generate unit tests for our checkout service at src/services/checkout.ts. Cover the cart total calculation with discounts, tax computation by state, inventory reservation, and the Stripe charge flow. Use vitest as the test runner. Mock external dependencies but test the actual business logic.
The generator produces tests covering edge cases that manual test writing often misses: empty carts, negative quantities, expired discount codes, and tax-exempt states.
The test generator produces tests covering edge cases that manual writing often misses:
// src/services/__tests__/checkout.test.ts
describe("cart total calculation", () => {
it("applies percentage discount before tax", () => {
const cart = createCart([
{ sku: "WIDGET-01", price: 29.99, quantity: 2 },
{ sku: "GADGET-03", price: 49.99, quantity: 1 },
]);
const total = calculateTotal(cart, { discount: { type: "percent", value: 15 } });
expect(total.subtotal).toBe(109.97);
expect(total.discount).toBe(16.50);
expect(total.taxable).toBe(93.47);
});
it("rejects negative quantities", () => {
expect(() => createCart([{ sku: "X", price: 10, quantity: -1 }]))
.toThrow("Quantity must be positive");
});
it("handles empty cart without crashing", () => {
const total = calculateTotal(createCart([]), {});
expect(total.subtotal).toBe(0);
expect(total.tax).toBe(0);
});
});
2. Set up API mocking with MSW
Replace flaky network calls with deterministic mock responses that mirror real API behavior.
Set up MSW to mock our backend API during tests and local development. Create handlers for the /api/products, /api/cart, and /api/checkout endpoints. Include realistic response delays (200-400ms) and error scenarios: 401 unauthorized, 429 rate limited, and 500 server error. The mocks should work in both the test runner and the browser.
MSW intercepts requests at the network level, so the application code does not know it is talking to a mock. This catches bugs that higher-level mocking misses, like incorrect request headers or missing content-type handling.
3. Add E2E tests with Playwright
Test complete user flows through the real browser to catch integration issues.
Write Playwright E2E tests for the critical purchase flow: browse products, add to cart, apply discount code, enter shipping address, complete checkout, and verify the confirmation page. Use page object models to keep tests maintainable. Run tests in Chromium, Firefox, and WebKit.
4. Configure regression detection
Catch visual and functional regressions automatically on every pull request.
Set up regression testing that runs on every PR. Compare screenshots of key pages against baseline images to catch visual regressions. Run the full E2E suite and compare API response shapes against recorded snapshots. Fail the PR if any regression is detected and post a diff report as a PR comment.
Real-World Example
An e-commerce team was spending 4 hours per week debugging flaky tests that failed because the staging API was down or returned unexpected data. After setting up MSW, test suite reliability went from 78% to 99.6% pass rate. The test generator added 340 unit tests to previously untested modules in two days. Playwright E2E tests caught a CSS regression where the checkout button was hidden behind a banner on mobile Safari -- a bug that three rounds of manual QA had missed. The regression tester blocked a PR that accidentally changed the tax calculation rounding from banker's rounding to floor rounding, which would have undercharged tax on 12% of orders.
Tips
- Generate unit tests for modules that handle money, authentication, and data integrity first. Bugs in those areas have the highest business impact.
- Keep MSW handlers in a shared
mocks/directory used by both the test runner and the local dev server. This guarantees the mock responses you test against match what developers see during development. - Use Playwright's
page.waitForLoadState("networkidle")sparingly. Prefer waiting for specific elements to appear, which makes tests faster and less flaky. - Run regression screenshot comparisons on a consistent CI environment. Local screenshots will differ from CI due to font rendering and OS differences.