Back to blog
Verification
Superpowers
Claude Code

The One Rule That Stops Agents from Inventing Tests

Thang Doan
Thang Doan

An agent writes a function, then writes a test for it. The test passes immediately. The agent reports success.

The test proves nothing. It was written by the same context that wrote the code, biased toward making itself look correct. It tests the implementation, not the behavior. It misses the edge cases the implementation missed.

This is the most expensive false positive in AI-assisted engineering. The green checkmark reads as done. The bug ships.

The iron law

No production code without a failing test first.

If you wrote code before the test, delete it. Start over. Do not keep it as "reference". Do not "adapt" it while writing tests. Do not look at it. Delete means delete. Implement fresh from the tests.

Agents will resist this rule. They will say "I already wrote the implementation, let me just write the test now". The answer is no. The implementation came from a biased context. The test written after it will be biased to match.

The red phase

Write one minimal test showing what should happen.

The test has a clear name. Real code, no mocks unless unavoidable. It tests one behavior. If the name has "and" in it, split it into two tests.

Run the test. Watch it fail. Confirm the failure is because the feature is missing, not because of a typo or a test setup error. If the test passes, you are testing existing behavior. Fix the test. If it errors instead of failing cleanly, fix the error first.

Watching the test fail is the step agents skip. They run the test, see red, assume the test is broken, and "fix" it. The fix is making the test green by changing the assertion. Now the test is meaningless.

The discipline is reading the failure. The failure must say "expected X, got undefined" or similar. If it does not, the test itself is wrong.

The green phase

Write the simplest code that passes the test.

Not the most elegant code. Not the most general code. Not the code that anticipates the next three requirements. The simplest code.

YAGNI applies ruthlessly here. Over-engineering during green is a failure mode. The agent wants to add a parameter for "future flexibility". No. The test does not require the parameter. Do not add it.

If you cannot resist improving the code, you are not writing tests first. You are writing code and backfilling tests. The distinction is the entire point.

The refactor phase

Only after green.

Remove duplication. Improve names. Extract helpers. Keep tests green throughout. Do not add behavior during refactoring.

Agents skip refactor because the work is already "done" when green. The result is code that works but rots. Refactor is what makes the next change cheap. Skipping it is technical debt on a timer.

The rationalizations and why they fail

"I will write tests after." Tests written after code pass immediately. Passing immediately proves nothing about whether you tested the right thing.

"Too simple to test." Simple code breaks too. The test takes thirty seconds. The debug session when it breaks in production takes hours.

"Deleting hours of work is wasteful." Sunk cost fallacy. Keeping unverified code is technical debt. The hours are already gone. The question is whether you also ship the debt.

"TDD will slow me down." TDD is faster than debugging in production. The test-first cycle catches bugs before commit, when they are cheap. Debugging the same bugs after merge is slower.

"Tests-after give coverage too." They give coverage. They do not give proof. Coverage measures lines executed. Proof measures behavior verified. They are different things.

The agent-specific failure mode

Agents have a particular pattern that humans do not.

The agent writes the implementation. It then writes a test that imports the implementation, calls it with one obvious input, and asserts the obvious output. The test passes. The agent reports done.

The test did not exercise any edge case. It did not verify error handling. It did not check what happens when the input is null, empty, large, malformed. It confirmed that the function does the obvious thing in the obvious case. Which it always did, because the function and the test came from the same context.

The fix is the iron law. Test first. Watch it fail. The failure pattern reveals what the test is actually checking. Then write minimal code. The test now exercises behavior, not implementation.

When TDD is theater

For a one-line change to a constant, TDD is theater. The test costs more than the bug.

For a UI tweak with no logic, TDD is theater. The test is a snapshot that breaks on every unrelated change.

For a refactor with no behavior change, TDD is theater. The existing tests are the proof.

The judgment is whether the change introduces behavior. If yes, TDD. If no, the existing tests cover it.

The mistake is applying TDD to everything. The bigger mistake is applying it to nothing. Most code lies between the extremes, and most code benefits from test-first.

What changes for agent review

When you review an agent's PR, ask one question first: did the test exist before the code?

You cannot always tell from the diff. But you can ask the agent. If it says "I wrote the implementation first, then the test", reject the PR. Ask for the test to be deleted and rewritten first.

The agent will complain. The complaint is the signal. The discipline is the fix.

Recommended for you

Enjoyed this article?

Subscribe for new articles. No spam. Unsubscribe anytime.

By subscribing you agree to receive the newsletter. See the Privacy page.