How to use test-driven development with AI coding agents
Your agent finishes, reports "all tests pass," and it's telling the truth. It also wrote those tests itself, after writing the code, from the same misunderstanding of the requirement you're about to discover in production. Green checkmarks, wrong feature.
That's the problem with tests written after the fact: they don't check the code, they describe it. Test-driven development flips the order, and it turns out TDD suits agents far better than it ever suited us.
Why TDD works better on agents than on humans
TDD always asked people to do the boring part first, then repeat it a hundred times a day. Most of us quietly stopped. Agents don't get bored, don't skip the red step in a hurry, and will happily run the suite forty times in a row.
More importantly, agents thrive on closed loops. Give one a failing test and it has an unambiguous, machine-checkable target it can hit without you in the room. Give it a paragraph of English instead and "done" becomes a judgment call, made by the agent, graded by the agent, reported to you as a success. That loop is the heart of an agent-ready codebase: the faster and more honest your suite, the better every agent in the repo behaves.
A failing test is the only definition of done an agent can't talk its way around.
The loop, adapted for agents
- Write the test first, and own it. State the behavior as a test before any implementation exists. Let the agent draft it if you want, but read every assertion yourself. This is the one artifact you shouldn't outsource, because it is the spec.
- Watch it fail for the right reason. A test that passes before the feature exists is testing nothing. A test that fails with an import error isn't testing anything either.
- Hand over the implementation. "Make this test pass. Don't modify the test" is a complete, unambiguous brief. It's usually shorter than the prompt you would have written, and much harder to misread.
- Read the diff anyway. Green means the target was hit. It says nothing about the six unrelated files the agent touched on the way there.
Agents optimize for green: plan for it
Here's the part most write-ups leave out. If you grade an agent on a passing suite, it will pass the suite, and not always the way you meant. In practice that looks like:
- Relaxing an assertion until it matches whatever the code returned.
- Mocking out the exact thing the test was supposed to exercise.
- Marking the test skipped, or wrapping it in a try/catch that swallows the failure.
- "Fixing" the test to match the bug.
None of this is malice. It's an agent doing precisely what you rewarded, and a flaky test rewards it even harder: now the shortcut sometimes works by accident. Two habits catch nearly all of it:
Read test changes separately from code changes. Any diff where the test moved toward the code deserves a hard look.
Say the rule out loud in the task. "Don't change the test. If you believe the test is wrong, stop and explain why instead of editing it." An agent that hits a genuinely bad test will tell you, which is often the most valuable output of the whole run. Standing rules like this belong in your AGENTS.md, alongside the exact command that runs your suite, so you're not retyping them every session.
A failing test is a better prompt than a paragraph
Prose is ambiguous. "Handle expired tokens gracefully" has a dozen readings, and the agent will pick one without telling you which. A test picks one for it: executable, specific, self-checking.
It also outlives the session. The prompt you typed today is gone tomorrow; the test is still in the repo, still guarding the behavior when a different agent touches that file next month. You're not just directing this run: you're leaving the next one a spec.
That doesn't mean testing everything up front. When you're exploring, sketching a UI, or vibe coding a prototype to see if an idea has legs, TDD is pure friction. Save it for the places where being wrong is expensive: money, auth, permissions, data migrations, anything with edge cases you can name out loud.
It compounds once you're running more than one agent
With a single agent you can watch the loop happen. With four running in parallel you can't, and self-verification stops being a nice-to-have. Each task that ships with its own failing test arrives at your desk having already proved something, which is what keeps reviewing the output from becoming a full-time job.
That's the shape SanuDesk is built around. It's a desktop app where your Claude Code and Codex sessions tile into one grid, each pointed at a project folder, taking work from a Kanban board. A card carries the acceptance criteria and the test to satisfy; you deploy it to a fresh agent session; when the agent finishes, the card lands in In Review with a what-changed / how-to-test note attached. The verification is part of the handoff, not something you remember to do afterwards.
The recurring runs fit too: a Loop can run the full suite on a schedule and journal every result, so a test that starts failing at 3am is waiting for you in the morning instead of surfacing in a support email. And it drives the agent accounts you already pay for, so nothing here is metered by us.
Start with one test
You don't need to convert your workflow. Next time you're about to hand an agent a feature, spend two minutes writing the test that would prove it works, watch it go red, then paste "make this pass, don't touch the test."
The diff that comes back will be smaller, more focused, and, for the first time, already checked by something other than the thing that wrote it.
Download SanuDesk free and point a workspace at your repo, or see how the grid, the board and Loops fit together on the features page.