Startups need to ship fast. They also need software that works. Most testing advice comes from enterprise teams with 200 engineers and does not translate to a team of 3-8 developers trying to hit a launch date. Here is the testing strategy we use on real projects.
The Pragmatic Testing Pyramid
The classic testing pyramid says lots of unit tests, some integration tests, and few end to end tests. That is directionally correct, but startups should flip the priority order.
Start with end to end tests for critical paths. Before writing a single unit test, write E2E tests for your most important user flows: signup, login, core value action (the thing users pay for), and payment. These 4-6 tests catch the bugs that actually cost you customers.
Add integration tests for data flows. API endpoints, database queries, and third party integrations. These catch the bugs that break silently, your Stripe webhook handler returning 200 but not updating the subscription, or a database migration that drops a column your API still reads.
Unit tests for complex logic. Pricing calculations, permission checks, state machines, and anything with branching logic. Skip unit tests for simple CRUD operations, the integration tests cover those.
What Not to Test
Testing everything is a waste of time. Skip: UI layout tests (they break constantly and catch nothing), simple getter/setter functions, framework boilerplate (Next.js routing, React rendering), and database schema validations (your ORM handles this).
The goal is not 100% code coverage. It is confidence that your critical paths work. We typically aim for 60-70% coverage, concentrated on the code that matters.
Tools That Work
Vitest for unit and integration tests. Fast, TypeScript native, and compatible with Jest's API. Drop in replacement that runs 2-5x faster than Jest.
Playwright for end to end tests. Tests real browser behavior, supports Chrome, Firefox, and Safari. The auto waiting mechanism eliminates the flaky timer hacks that plague Cypress tests. Built in screenshot and video recording for debugging failures.
MSW (Mock Service Worker) for mocking external APIs in integration tests. Intercepts network requests at the service worker level, your code makes real fetch calls, MSW intercepts them. No need to mock your HTTP client.
Testing Library for component tests. Tests components the way users interact with them, by text content, roles, and labels. Discourages testing implementation details.
CI Integration
Every test suite should run on every pull request. No exceptions. Here is the setup we use on projects like Traderly:
Pre commit: Lint and type check only. Fast enough to run on every save. Catches typos and type errors before they hit CI.
Pull request: Full unit and integration test suite. Target: under 3 minutes. If tests take longer than 5 minutes, developers stop waiting and merge without checking results.
Pre merge: E2E test suite against a preview deployment. These can take 5-10 minutes and that is fine, they run after code review is complete.
Post deploy: Smoke tests against production. Hit your 3-4 most critical endpoints and verify they return expected responses. This catches deployment specific issues (environment variables, infrastructure changes).
Set up your CI/CD pipeline to run tests in parallel, Vitest supports worker threads out of the box, and Playwright can shard E2E tests across multiple containers.
Dealing With Flaky Tests
Flaky tests, tests that pass sometimes and fail other times, are the number one killer of testing culture. When tests are unreliable, developers stop trusting them and start ignoring failures.
Identify flakes immediately. Track test results over time. Any test that fails more than once in 20 runs without code changes is flaky. Quarantine it. Move it to a separate test suite that runs but does not block merges.
Common causes and fixes. Timing dependencies: use explicit waits (Playwright's auto wait) instead of sleep statements. Shared state: ensure each test creates its own data and cleans up after itself. Network dependencies: mock external services with MSW instead of hitting real APIs. Database ordering: never rely on insertion order, sort results explicitly.
The 48-hour rule. When a flaky test is identified, fix it within 48 hours or delete it. A flaky test that sits in quarantine for months is technical debt that teaches your team to ignore test failures. Better to have no test than a test nobody trusts.
The Cost of Not Testing
Bugs found in development cost 10 minutes to fix. Bugs found in production cost hours, debugging, hotfixing, deploying, and apologizing to affected users. Bugs found by users who churn cost you months of revenue.
A startup spending 20% of development time on testing ships slightly slower per sprint but dramatically faster over 6 months. We have seen this pattern repeatedly: teams that skip testing move fast for the first 3 months, then spend months 4-12 debugging regressions, afraid to refactor, and manually verifying every change. Teams that invest in testing from the start maintain consistent velocity throughout the year. Without tests, every change is a gamble. With tests, you refactor confidently, onboard new developers faster, and ship without the 2am anxiety of wondering what broke.
Getting Started Today
If you have zero tests, here is your first week:
Day 1-2: Set up Vitest and Playwright. Write one E2E test for your signup flow. Get it running in CI.
Day 3-4: Write integration tests for your 3 most important API endpoints. Mock external services with MSW.
Day 5: Write unit tests for your most complex business logic function. The one everyone is afraid to touch.
You now have a testing foundation that catches 80% of regressions. Expand from here as you build new features, every new feature should include tests for its critical paths.
Our DevOps practice sets up testing infrastructure as part of every project. Good testing is not a luxury, it is how you ship fast without breaking things.
Need help setting up a testing strategy? We will get your CI pipeline catching bugs before your users do.