Generated tests can pass for the wrong reason — a trivial assertion that would pass no matter what the implementation does isn't coverage, it's the appearance of coverage.
1Some Assertions Prove Almost Nothing
expect(result).toBeDefined() or .toBeTruthy() pass for nearly any non-empty, non-zero output — they don't check the result against an actual expected value, so they'd pass even if the underlying logic were completely wrong.
2Ask for Concrete Cases From the Real Spec
Naming the specific cases a spec calls for — normal input, empty input, the failure case — and asking for a concrete expected value per case produces tests that would actually fail if a future change broke that specific behavior.
3Step-by-Step Breakdown
Generated Tests Can Pass for the Wrong Reason. Ask Claude Code to add tests for exportTasksAsCsv, and it's easy to end up with tests that only exercise the happy path — or worse, tests written to match whatever the implementation already does, which would pass even if the implementation is wrong.
Ask for the Real Spec's Cases, By Name. features/export.md named specific cases: a normal list, an empty list, and an export failure. Asking Claude Code to write a test per named case — with a concrete expected output — produces tests that would actually catch a real regression, not just tests that exist.
Why does expect(result).toBeDefined() prove almost nothing about whether exportTasksAsCsv is actually correct?
- →It would pass even if the function returned completely wrong output, as long as it returned something — the assertion doesn't check the actual content against an expected value.
- →It's correct, just slower to run than other assertions.
Turn a Weak Assertion Into a Real One. Practice the exact rewrite: take a trivial, passes-no-matter-what assertion and turn it into one that would actually fail if the implementation were wrong.
Trust Comes From the Assertion, Not the Existence of a Test. A green test suite is only meaningful if the assertions inside it would actually fail on wrong behavior. Next module: extending Claude Code itself with custom commands, subagents, hooks, and MCP servers.
Level Up 🚀
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
N/A — this workflow runs in the terminal test runner.
N/A — this workflow runs in the terminal test runner.
N/A — this workflow runs in the terminal test runner.
N/A — this workflow runs in the terminal test runner.
Accessibility (A11y)
1Name Test Cases in Plain Language Matching the Spec
Test names like 'exportTasksAsCsv on an empty list returns just headers' double as living, screen-reader-friendly documentation of the spec's actual requirements.
test('on an empty list returns just headers', ...)SEO Implications
- 1
Target 'AI-generated tests are useless' and 'how to review AI-generated test coverage' separately
Skeptical developers search for the failure mode by name; teams already using the tool search for how to actually catch it.
Best Practices
Ask for One Test Per Named Spec Case, With a Concrete Expected Value
Prompting for coverage of each specifically named case (normal, empty, failure) with a real expected output — rather than a generic 'add tests' request — produces assertions strong enough to actually catch a regression.
Frequent Bugs
Accepting generated tests that only check a result 'exists' or is 'truthy', which would pass even on completely broken output.
Rewrite or request assertions that check a concrete expected value for a specific, named input — the test should fail if the real behavior is wrong.
Real-World Examples
The Test That Passed on a Broken Function
A generated test used `expect(csv).toBeDefined()` for the export feature. A later change accidentally broke the totals row calculation, but the test still passed — it had never actually checked the CSV's content. A rewritten test asserting the exact expected row caught the regression immediately.
// Passed on broken output: expect(csv).toBeDefined()
// Caught the regression: expect(csv).toContain('Total,5')