DEV Community

Zac
Zac

Posted on

Claude Code writes tests that pass but don't test anything. Here's the prompt that fixes it.

Ask Claude Code to add tests and you'll often get something like this:

test('should process order', () => {
  const result = processOrder(mockOrder)
  expect(result).toBeDefined()
  expect(result).not.toBeNull()
})
Enter fullscreen mode Exit fullscreen mode

That test is basically useless. It confirms processOrder doesn't throw and returns something. It won't catch a bug where the order total is calculated wrong, the status isn't set correctly, or the timestamps are missing.

Claude generates these because "write tests for this function" is ambiguous. Defined-and-not-null is technically a valid response to that instruction.

The prompt that changes it

Add this to your CLAUDE.md:

When writing tests:
- Test specific return values, not just that something returned
- Every test must fail if the core behavior changes
- No expect(result).toBeDefined() or expect(result).not.toBeNull() as the primary assertion
- For each test, write a comment explaining what bug it would catch
Enter fullscreen mode Exit fullscreen mode

The last line is the important one. Forcing a comment about "what bug this catches" makes Claude reason about what the test is actually for before writing it. The assertion quality improves significantly.

What good looks like

Same function, with that rule in place:

// Catches: wrong total calculation (tax not included)
test('order total includes tax at configured rate', () => {
  const order = processOrder({ items: [{ price: 100, qty: 1 }], taxRate: 0.1 })
  expect(order.total).toBe(110)
})

// Catches: status not set on new orders
test('new orders start with pending status', () => {
  const order = processOrder({ items: [{ price: 50, qty: 2 }] })
  expect(order.status).toBe('pending')
})

// Catches: missing created timestamp
test('processed orders have a created timestamp', () => {
  const before = Date.now()
  const order = processOrder({ items: [{ price: 10, qty: 1 }] })
  expect(order.createdAt).toBeGreaterThanOrEqual(before)
  expect(order.createdAt).toBeLessThanOrEqual(Date.now())
})
Enter fullscreen mode Exit fullscreen mode

Each test fails for a specific, named reason. That's the bar.

The follow-up rule

Claude will sometimes still write a weak test if the function is complex. Add this as a second rule:

After writing tests, review each one: if you changed nothing in the implementation
except removing the return statement, would this test still pass? If yes, rewrite it.
Enter fullscreen mode Exit fullscreen mode

This is the "empty return" check. A test that passes even when the function returns nothing is a test that tests nothing.

Why this matters more with agents

When Claude Code runs autonomously across multiple sessions, it writes tests as checkpoints. If those checkpoints don't actually verify behavior, they give you false confidence that things are working — and you find out they weren't when something breaks in production.

The prompts above take 2 minutes to add to your CLAUDE.md and catch most of it.


I have ~25 prompts like this in the Agent Prompt Playbook — instructions that change Claude's default behavior in specific, predictable ways. builtbyzac.com if you want them.

Top comments (0)