The worst test suite I ever inherited had 400 tests, and I trusted about six of them. The rest were either testing implementation details nobody cared about, duplicating each other, or so tightly coupled to internal function names that a harmless refactor broke thirty tests for no real reason. Reading that codebase taught me more about what not to do than any greenfield project ever has.
So when you're starting from zero, the goal isn't "write a lot of tests fast." It's building a suite you'll still trust a year from now. If you're new to this, getting the software testing basics right early matters more than covering everything - learning how to build a first test suite from scratch teaches you what to prioritize in a way that inheriting someone else's bloated suite never will. Here's roughly how I'd approach it.
Start with what would actually hurt if it broke
Before writing a single test, list the handful of things that would be genuinely bad if they silently broke - checkout completing, auth working, the core thing your product does actually happening. Not every function, not every branch. Just the stuff where a silent failure costs you money, users, or trust.
This list is usually shorter than people expect. Five to ten flows for most early-stage products. That's your actual test suite's job in the first few months, not "100% coverage."
Unit tests for logic, not for plumbing
Unit tests are for things with actual decision-making in them - pricing calculations, validation rules, state transitions, anything where "given this input, is the output correct" is a real question with a wrong answer possible. They're fast, they're cheap, and they should make up the bulk of your suite.
Skip unit-testing pure plumbing: a function that just calls another function and returns its result doesn't need its own test. That's the kind of test that pads a coverage number without catching anything real, and it's exactly the kind of test that made that 400-test suite so hard to trust.
Integration tests for the seams
This is where most early suites are too thin. Unit tests tell you your pricing function is correct in isolation. They don't tell you the checkout flow actually calls it correctly, passes the right currency, or handles what happens when the payment provider returns an error you didn't expect.
The seams between your code and everything external - your database, third-party APIs, other internal services are where bugs actually like to hide, because that's where two sets of assumptions meet and don't always agree. This is also the hardest category to get right early on, because hand-writing every plausible external response (success, timeout, malformed payload, rate limit) is a lot of manual work, and most teams just don't do it, which is part of why integration bugs are so common in production even in codebases with decent unit coverage. Tools that can record real API traffic and replay it as test cases - Keploy is one - exist specifically to close that gap without hand-writing every scenario, which is worth knowing about before you spend a week writing mocks by hand.
Don't test the framework
A depressing amount of early test-writing time goes into testing things the framework already guarantees - does this React component render, does this route return a 200. If your framework is reasonably mature, trust it. Test your logic, not its plumbing.
Make flaky tests a same-week problem, not a someday problem
The first flaky test in a new suite is a fork in the road. Fix it now, while the codebase is small enough that the cause is easy to find, or let it slide and watch the team's trust in the whole suite erode as more join it. I've written about this before - flaky tests are almost never actually about the test itself, they're usually pointing at shared state, a race condition, or an environment assumption nobody wrote down.
Let the suite grow with real usage, not just imagination
Whatever you write on day one will cover the bugs you could imagine. It won't cover the malformed payload from a partner's outdated client, or the auth token format some old integration still sends. As the project gets real traffic, treat production incidents as test-suite input - every bug that reaches production and wasn't caught is a gap in the suite, and turning it into a regression test is the cheapest way that gap ever gets closed.
The suite you actually want in six months
Not the biggest one. The one where a red result still means something, where the team fixes failures instead of quietly muting them, and where adding a new feature doesn't mean an afternoon fighting tests that were testing the wrong thing to begin with. Start small, aim it at what actually matters, and let it grow from real usage rather than a coverage target.
Top comments (0)