
Test coverage is the thing every developer knows they should have and keeps pushing off. You ship a feature, tell yourself you'll write tests later, and "later" arrives in the form of a 2 AM production incident. Claude Code removes the friction entirely: hand it a file, give it one command, and walk away with a test suite.
This is for developers who are comfortable with JavaScript/TypeScript and have Jest (or a similar runner) installed, but keep skipping test coverage because writing tests feels like a second job.
The Problem: Tests Feel Like Overhead
Writing tests for code you just finished feels punishing. You already know what the function does — you just built it. The test syntax is different from your production code, setting up mocks takes time, and edge cases are easy to forget. So the test file never gets created.
The deeper problem is that untested code becomes a minefield. Every refactor carries a silent question: did I break something three files away? Without a test suite, the only answer is "ship it and find out."
The first thing most developers try is writing a few happy-path tests — the obvious input/output cases. That covers maybe 40% of real failure modes. Null inputs, empty arrays, values at integer boundaries, and async rejection paths go untouched. The test file gets committed, coverage looks passable, and nobody touches it again.
The Fix: One Command, One File
Open your project in Claude Code, point it at a source file, and run:
claude "Write Jest tests for every function in this file. Cover both happy paths and error cases."
That's the entire workflow. On a 50-line utility file, a complete test file appears in under 30 seconds. In practice, around 95% of the generated tests pass without modification — the remainder usually flag real edge cases that the source code wasn't handling correctly.
Here's a more targeted version that specifies where the output should go:
claude "Analyze utils/calculator.js and create a new test file at utils/calculator.test.js"
Claude reads the module's export structure, infers parameter types from usage, and writes describe/it blocks that mirror your project's existing test conventions if any are present.
Verification — after Claude writes the file, run your test suite immediately:
npx jest utils/calculator.test.js --verbose
Expected output:
PASS utils/calculator.test.js
add()
✓ returns sum of two positive numbers (3ms)
✓ handles negative operands (1ms)
✓ returns 0 when both inputs are 0 (1ms)
divide()
✓ returns correct quotient (1ms)
✓ throws when divisor is zero (2ms)
Test Suites: 1 passed, 1 total
Tests: 5 passed, 5 total
Section 3: Edge Cases — Where Claude Actually Earns It
The happy-path tests are easy. The valuable part is what Claude generates without being asked: the cases that break things in production but never appear in a spec.
Think of a delivery driver who marks a package delivered. That works 95% of the time. The cases nobody planned for: building access denied, wrong floor, rain-soaked mailroom. Claude generates the software equivalent automatically.
Run this to specifically target the boundary conditions:
claude "Strengthen the test file with edge cases. Must include: null input, undefined, empty array, negative numbers, and boundary values at INT_MAX."
What gets generated for a typical calculateDiscount(price, rate) function:
describe('calculateDiscount()', () => {
// Happy path
it('returns correct discount for valid inputs', () => {
expect(calculateDiscount(100, 0.2)).toBe(20);
});
// Edge cases Claude adds automatically
it('returns 0 when price is 0', () => {
expect(calculateDiscount(0, 0.2)).toBe(0);
});
it('throws TypeError when price is null', () => {
expect(() => calculateDiscount(null, 0.2)).toThrow(TypeError);
});
it('throws when rate exceeds 1', () => {
expect(() => calculateDiscount(100, 1.5)).toThrow(RangeError);
});
it('handles negative price gracefully', () => {
expect(calculateDiscount(-50, 0.1)).toBe(0); // or throws — depends on your spec
});
it('returns 0 discount when rate is 0', () => {
expect(calculateDiscount(100, 0)).toBe(0);
});
});
The last test in that list — rate = 0 — is the one most developers forget. It's never in the spec because it seems obvious, and it's exactly the case that causes a divide-by-zero in a refactored version three months later.
Adding Tests to Legacy Code
This is where the workflow pays the biggest dividend. Legacy code without tests can't be safely refactored — you have no safety net. The standard advice is "add tests before touching anything," which is a good idea that nobody follows because it takes too long.
claude "Analyze src/legacy/orderProcessor.js and generate a full test suite. The file has no existing tests. Infer the expected behavior from the function names and implementations."
Claude reads the implementation and reverse-engineers the intended contract. For a function called applyBulkDiscount(), it infers that positive integers should produce a reduction, that zero quantity should be handled, and that negative quantities are likely erroneous. The generated tests capture that inferred contract.
Once those tests exist and pass, refactoring becomes a different activity. You change a line, run npx jest, and get immediate confirmation that nothing broke. The anxiety of "what did I break?" disappears.
Environment Differences to Watch
| Environment | Gotcha | Fix |
|---|---|---|
| Node < 18 | TextEncoder not global |
import { TextEncoder } from 'util' in setup |
| Docker (Alpine) | jest --coverage needs lcov |
Add apk add lcov to Dockerfile |
| macOS with M-series | Native modules rebuild on jest --runInBand |
Set testEnvironment: 'node' in jest.config |
| ESM projects | import syntax fails in Jest |
Use --experimental-vm-modules flag |
One pattern worth knowing: if your project uses ES modules ("type": "module" in package.json), tell Claude explicitly:
claude "Write Jest tests for this ESM module. Use import syntax and configure for --experimental-vm-modules."
Claude will adjust the generated file accordingly rather than outputting CommonJS require() calls that break on execution.
Closing
Not knowing how to write tests is no longer a blocker. Hand Claude a file, give it a single command, and a test suite exists. More importantly, it's a test suite that covers the edge cases you would have skipped.
The shift this creates is concrete: instead of shipping code and hoping nothing breaks, you have a guard rail that runs in milliseconds. Refactoring goes from anxiety-inducing to routine. The next logical step is wiring that guard rail into CI so tests run automatically on every push — Claude can generate the GitHub Actions workflow for that too.
🐦 Faster updates on X: @baegseungh7061
📚 More in this series: Code Intro
💌 Subscribe: Follow on X or grab the RSS
댓글
댓글 쓰기