Getting Claude to write tests is easy. Getting Claude to write tests that actually catch bugs is harder.
The problem with default test generation
When you ask Claude to "add tests," it writes tests for the happy path. The function is called with valid input, the output is what the docstring says. This achieves coverage numbers but doesn't catch much.
Tests get useful when they check: invalid input, boundary conditions, concurrent operations, error paths, and integration with other components.
The better prompt
Instead of "add tests for this function," try:
"Write tests for calculateInvoiceTotal. Cover:
- Normal case: valid items with tax
- Empty items array
- Items with zero quantity
- Tax rate of 0
- Tax rate over 100%
- Negative item prices
- Integer overflow risk on large quantities
- Rounding: total should be exact to 2 decimal places"
Specifying the cases gets you tests that check what actually goes wrong in production.
Mutation testing prompt
"Write tests for this function. Your goal is to write tests such that if I changed any single line of the implementation — a comparison operator, a return value, a condition — at least one test would fail."
This framing gets Claude to write tests that verify the actual logic rather than the outputs.
Characterization tests for legacy code
For existing code with no tests: "Write characterization tests for this function. The goal is not to test that it's correct, but to document what it currently does so I can refactor safely. If the function has surprising behavior, note it in a comment above the test."
This gets a test suite that captures the current behavior (bugs and all) so you can refactor without breaking anything unintentionally.
The review checklist
- Do the tests actually assert something meaningful, or are they just calling the function?
- Are negative cases covered?
- Are the edge cases you care about tested?
- Are the assertions specific enough that a wrong value would fail the test?
Top comments (0)