Testing Strategy for Solo Devs
When you're building alone, testing is a weird problem. You don't have a QA team. You don't have another engineer to catch your mistakes in code review. But you also don't have time to write a test for every edge case in the spec.
I shipped AssessAI with 630+ unit tests as a solo dev. Here's the framework I used to decide what to test and what to skip.
the testing pyramid is wrong for solo devs
The classic testing pyramid says lots of unit tests, fewer integration tests, even fewer E2E tests. That's advice for teams with dedicated QA engineers and CI pipelines that run thousands of tests in parallel.
For a solo dev, I flip the priorities:
- Integration tests for data flow — does the right data reach the right place?
- Unit tests for business logic — do calculations, transformations, and validations produce correct output?
- E2E tests for critical paths — can a user actually sign up, take an assessment, and see their score?
Integration tests give you the most confidence per line of test code. A single test that creates a user, starts an assessment, submits responses, and checks the score covers more surface area than 50 unit tests on individual functions.
what I always test
Data transformations. Any function that takes data in one shape and produces it in another. These are cheap to test and catch real bugs:
describe("normalizeMarkdown", () => {
it("converts escaped newlines to real newlines", () => {
expect(normalizeMarkdown("line1\\nline2")).toBe("line1\nline2");
});
it("handles null input", () => {
expect(normalizeMarkdown(null)).toBe("");
});
});Validation logic. Schema validation, input sanitization, permission checks. These are your system boundaries. If they break, bad data gets in and corrupts everything downstream.
State machines. Assessment flow (idle -> in_progress -> submitted -> scored), auth (anonymous -> authenticated -> admin). State transitions have specific rules. Tests enforce them.
Anything involving money or scoring. If it touches payment processing, pricing logic, or evaluation scores, test it heavily. Bugs here cost trust.
what I skip
Pure UI rendering. Does this component render a button? I can see that. Snapshot tests for layout are brittle and low-value. If the button disappears, I'll notice in 5 seconds of manual testing.
Third-party library wrappers. If I'm calling supabase.from('users').select(), I don't write a test for Supabase's query builder. It's tested. I test my logic around the result.
Config and constants. A file that exports { MAX_QUESTIONS: 50, TIME_LIMIT_MINUTES: 60 } doesn't need tests. It's declarative. If the values are wrong, tests won't catch that — only product feedback will.
the TDD shortcut
I use TDD, but a specific flavor of it. I don't write tests for every function before implementing it. I write tests for the behavior I'm about to build:
- Write a test that describes the feature outcome
- Run it — it fails (red)
- Write the minimum code to make it pass (green)
- Refactor if needed
This is faster than writing all tests upfront because you only test what matters. The test describes the contract. The implementation fulfills it. If the test passes, the feature works.
Claude Code's tdd-guide agent enforces this. It literally won't let me write implementation code before a failing test exists. Annoying sometimes. Worth it always.
coverage targets
I aim for 80% on business logic modules. 60% overall. 100% on anything touching auth or scoring.
The 80% target is a signal, not a goal. If I'm at 78% and the remaining 2% would require mocking eight services to test an error path that can't happen in production, I skip it. Coverage is a proxy for confidence, not a number to maximize.
the one test that saves the most time
An integration test for your happy path. The one flow that 90% of users execute. For AssessAI, it's: create assessment -> invite candidate -> candidate starts -> candidate submits -> score appears.
If that test passes, the product works. Everything else is refinement.
Write that test first. Then build the features to make it pass. You'd be surprised how much of the system you cover with one well-designed integration test.
More in Software Dev
Git Workflow for Solo Founders
Trunk-based dev, feature branches, conventional commits. What works when you're the only person pushing code.
My Code Review Checklist
What I look for when reviewing code: correctness, edge cases, naming, testing. Lessons from leading a team at Blinq.
Docker for Developers, Not Ops
Dev containers, multi-stage builds, compose for local dev. The Docker knowledge that actually matters when you're writing code, not managing infrastructure.