Give a coding agent a failing test and it will make it pass. That is one of the best things about agents, and one of the most dangerous. The goal it is steering towards is a green check, and there are several ways to get one. Fixing the code is only one of them. Changing the test, mocking away the logic, or special-casing the exact input the test uses are others.
Tests are still the best tool you have for getting trustworthy code out of an agent. You just have to set them up so that the only way to green is the right one.
Why tests matter more with agents
An agent can't ask you every five minutes whether its change works. It needs a feedback loop it can run on its own, and the test suite is that loop. With fast, reliable tests, an agent can try, fail, read the error and fix, many times, without you. Without them, it is guessing, and you find out in review.
So the first investment is boring: one command that runs the tests, written in your rule file, that finishes fast and doesn't flake. A flaky test teaches the agent that failures are noise, and it will start working around real ones.
The tests-first workflow
- Write the tests first, or have the agent write them. Describe the behavior and ask for tests only, no implementation. Make sure they fail for the right reason.
- Review the tests. This is the cheapest review you will do all day. Tests are short, and they say exactly what “working” means. Are the cases right? Are the edge cases there? Is anything mocked that shouldn't be?
- Commit them. Now the specification is fixed, and any later change to it shows up clearly in the diff.
- Ask for the implementation, without touching the tests. Say it explicitly: “make these tests pass; don't modify the test files.”
- Run everything. Full suite, type checker and linter, not just the new tests.
For bug fixes, the same idea is even simpler: first a test that reproduces the bug, then the fix. A bug fixed without a failing test first is a bug that can come back unnoticed.
Protect the tests from the agent
“Don't modify the tests” in a prompt works most of the time. For the rest, make it visible or impossible:
- Make it visible in CI. A small check that lists test files changed in a pull request makes a modified test impossible to miss in review.
- Make it impossible during the session. A Claude Code
PreToolUsehook can block edits to test files while the agent is implementing. We show how in Claude Code hooks: guardrails that don't depend on the prompt. - Require an owner. A
CODEOWNERSentry on the test folders of critical code means a specific person signs off on any change to them.
# CI: list test changes in a pull request so reviewers can't miss them
git diff --name-only origin/main...HEAD | grep -E '(\.test\.|\.spec\.|/tests?/)' \
&& echo "::warning::This PR changes tests. Review them first." || trueWarning signs in agent-written tests
- The unit under test is mocked. The test passes, and proves nothing.
- Assertions got weaker.
toEqual(expected)becametoBeDefined(). - Only the happy path. No empty input, no error case, no boundary.
- Tests that can't fail. A
try/catcharound the assertion, or an async test that never awaits its promise. - Snapshots regenerated wholesale. The snapshot now records the bug as the expected output.
- Code that knows about the test. An
iffor the exact value the test uses is not a fix.
Cheap tests you already have
Types and linters are tests that run in seconds and that agents respond to very well. A strict type checker catches a whole class of agent mistakes, such as calling an API that doesn't exist or passing the wrong shape, before any test runs. If your codebase allows any everywhere, agents will use it everywhere. Tightening the rules gives agents a better feedback loop and gives you less to review.
For parsers, formatters and data transforms, property-based tests and golden files are worth a look too. They are hard for an agent to game, because the agent can't see every input in advance.
Turn every bug into a test, and a lesson
A regression test makes sure a fixed bug stays fixed in that code. It doesn't tell the next agent why the code is shaped the way it is, or warn it before it writes the same mistake in a different file. Write the test, and also write down the lesson in a place agents read: a rule file, or a team memory. The test catches the mistake at the end. The lesson stops it at the start.