Tests as Your Safety Net
Asking for tests that actually test something, and why they matter more when you did not write the code yourself.
Tests were always worth writing. They become considerably more valuable when you did not write the code yourself — because a test is the only mechanism that checks behaviour rather than checking your reading of it.
Why they matter more now
When you write code by hand, you build a mental model as you go. You know which branches exist because you typed them. With generated code you have a reviewed model, which is thinner — you read it once, quickly, and moved on.
A test closes that gap. It does not care how carefully you read; it runs the code and reports what happened.
Asking for tests that test something
Ask for “some tests” and you get tests that assert the code does what the code does — passing, useless, and mildly reassuring. Ask for behaviour and edge cases and you get something worth having.
Weak request
“Write tests for this function.”
Strong request
“Write tests covering: an empty cart, a single item, a coupon that exceeds the total, and two coupons applied together. Test behaviour through the public function, not internals.”
Write tests for src/lib/pricing.ts. Cover: the normal case, empty input, the boundary where adiscount exceeds the subtotal, and two discounts combined. Test through the exported functions only — do not testprivate helpers. Each test name should describe the behaviour,not the implementation.Make it fail first
A test that has never failed has not been shown to work. This matters more than usual with generated tests, because a subtly wrong assertion passes just as quietly as a correct one.
- 1
For a bug fix: write the failing test first
It proves you have reproduced the bug before you patch it, and proves the patch worked when it goes green.
- 2
For new code: break it deliberately
Change a value in the implementation and confirm the test fails. Then change it back.
Ten seconds. It is the difference between a test and a decoration.
What to actually test
You do not need full coverage, and chasing it wastes time on code where failure is obvious anyway. Spend the effort where a silent wrong answer is possible:
- 1Money, quantities, and dates
Off-by-one and rounding errors are invisible until they are expensive.
- 2Anything with branches
Every if is a path someone must have checked.
- 3Boundaries
Empty, one, many, and one-past-the-end. Most bugs live at the edges.
- 4Bugs you have already had
A regression test is the cheapest test you will ever write, because the case is already known.
What rarely needs tests: pure layout, thin wrappers around a library, and code the type checker already constrains.
Wire it into your check command
Tests only help if they run. Add them to the command you already ask the AI to run, and every future request inherits the safety net:
"scripts": { "test": "vitest run", "check": "npm run lint && npm run typecheck && npm run test && npm run build"}Make the change, then run npm run check and fix anythingthat fails. Do not stop until it is green.Key takeaways
- Generated code gets read, not reasoned through. Tests are what close that gap.
- Name the cases you want covered — “write some tests” produces decorative ones.
- Watch every test fail once. A test that has never failed has not been shown to work.
- Prioritise money, branches, boundaries, and bugs you have already had.
- Put tests in your check command so every later prompt benefits automatically.