Overview
Every codebase has some corner nobody's written tests for and everyone's afraid to touch. This workflow uses Cursor's agent mode with Claude Sonnet 4 to read a module, infer its invariants, and write a test suite that exercises real behavior — not the trivial happy path. Property-based tests are added where they'd actually catch bugs.
How it works
- Open the file. Cmd-K → 'Draft a test plan (bullet list) before writing any tests.'
- Review the plan. Prune redundant cases, add missing edge cases.
- Cmd-K → 'Now write the tests according to the plan.'
- For pure functions: 'Add fast-check property-based tests for {invariant}.'
- Run tests. For failures, ask Composer to either fix the code or update the test with justification.
Benefits
- Coverage that matters, not coverage for coverage's sake.
- Property-based tests catch bugs handwritten tests never would.
- The test plan becomes documentation of what the module does.
Use cases
- Adding tests to a legacy module before refactoring it.
- Onboarding a new engineer — they read tests, not code.
- Post-incident: writing regression tests to lock in a fix.
Step-by-step guide
Step 1: Choose the target module
Start with pure functions or single-responsibility modules. Avoid god-classes on first pass.
Step 2: Get a test plan first
Never let the model write tests without listing what it's going to test. This is where you catch redundancy and gaps.
Step 3: Test the plan yourself
Ask 'What would a malicious input to this function look like?' — include those cases in the plan explicitly.
Step 4: Generate the suite
Composer in agent mode with the plan pinned. Use Vitest or Jest — Claude does both fluently.
Step 5: Add property-based tests
For pure functions with clear invariants (sorting, encoding, math): fast-check or hypothesis.
Step 6: Run + iterate
For real failures, ask: bug or test wrong? Never let the agent fix code without you naming which.
Example
Function: parseHumanDate('yesterday'). Property test: 'For any date d, parse(format(d)) === d'. Handwritten test: parse('yesterday') at midnight boundary.