Should You Let AI Write Your Tests? An Honest Take
Yes, but only for the parts that don't decide whether your code is correct. AI is excellent at the mechanical layer of testing: scaffolding, mocks, boilerplate, filling coverage gaps. It's dangerous the moment it writes the assertions, because it tends to test what the code does, not what the code should do. That distinction is the whole argument.
I've been letting AI write tests on real projects for months now, and my opinion landed somewhere uncomfortable for both camps. The “never let a robot near your tests” crowd is wrong. So is the “generate the whole suite and move on” crowd. The truth is in the boring middle, and it took a few bugs slipping through green test runs for me to see exactly where the line sits.
What does AI actually do well in testing?
AI shines at the repetitive, low-judgment parts of a test file: setup, teardown, mock wiring, fixtures, and the dozen near-identical cases you'd otherwise copy-paste. In my testing, this is where it saves real time, the work that's tedious precisely because it requires no decisions, just patience and consistency.
Here's the kind of thing I happily delegate. Give it a function and a couple of example cases, and it'll fill out the obvious variations faster than I'd type them:
// I wrote the first case; AI filled the rest
describe('formatCurrency', () => {
it('formats whole numbers', () => {
expect(formatCurrency(1000)).toBe('$1,000.00');
});
it('formats decimals', () => {
expect(formatCurrency(1234.5)).toBe('$1,234.50');
});
it('handles zero', () => {
expect(formatCurrency(0)).toBe('$0.00');
});
it('handles negatives', () => {
expect(formatCurrency(-50)).toBe('-$50.00');
});
});
For boilerplate-heavy ecosystems (React component rendering tests, API route handlers, repetitive mock setup) this genuinely removes friction. The cost of writing a test drops, so I write more of them. That's a real win, and I won't pretend otherwise.
Where do AI-written tests actively hurt?
The failure mode that scared me: AI writes a test that asserts the current behavior, including the bug. If your function returns the wrong value, and the AI reads that function to write the test, it'll happily assert the wrong value as the expected one. The test passes. Everyone feels safe. Nobody is.
This happened to me on a date-handling utility. The function was off by a timezone, and the generated test asserted the off-by-a-timezone output, because that's what the code produced when the AI ran it mentally. Green check mark, shipped bug. The test didn't catch the problem; it certified it.
That's the core danger. A test's value is the gap between “what the code does” and “what it should do.” When AI derives the expected value from the code itself, that gap collapses to zero. You get coverage numbers without correctness, which is arguably worse than no test, because it buys false confidence.
The false-confidence problem
Coverage percentage measures lines executed, not behavior verified. AI is very good at making that number go up while teaching you nothing about whether the code is right. A suite that's 90% covered and 100% derived from the implementation is a suite that will never disagree with the implementation, which is exactly when you need it to.
Tests that lock in the wrong thing
There's a slower-burning version too. AI-generated tests often over-specify implementation details: exact call counts, internal call order, private intermediate states. Those tests pass today and then scream the moment you refactor, even when behavior is unchanged. You end up maintaining a suite that resists improvement instead of enabling it.
So how do I actually use it without getting burned?
My rule is simple: AI writes the structure, I write or verify the assertions. I never let it derive the expected value from the code under test. The expected value comes from the spec, the ticket, or my own head, somewhere outside the implementation. The AI handles everything around that decision.
Concretely, the workflow that's worked for me:
- Describe the behavior first, in words. I tell the AI what the function should do, not “look at this function and test it.” The prompt framing changes everything.
- Let it scaffold cases and mocks. Edge cases, error paths, boilerplate setup. This is its strong suit, so I lean on it hard.
- Read every assertion line by line. If I can't independently say why an expected value is correct, I rewrite it. This is the non-negotiable step.
- Write the critical test first, by hand. For the one behavior that actually matters, I author the assertion myself, then let AI fill the surrounding cases.
Does test-driven development change the answer?
It changes it a lot, and for the better. When I write the test before the code, the AI can't assert the bug, because the buggy code doesn't exist yet. The expected values come from intent, not implementation. In my experience this is the single safest way to bring AI into a test workflow.
TDD flips the dangerous dynamic on its head. Instead of “here's my code, write tests that agree with it,” you're saying “here's what correct looks like, now make code match.” The AI is great at the second part and structurally prevented from the first failure mode. If you only adopt one habit from this post, make it this one.
My honest verdict
I'd let AI write my tests again tomorrow, with eyes open. It lowered the cost of testing enough that I test more, which is a genuine quality gain. But I treat every generated assertion as a claim to verify, not a fact to trust, because a test I didn't think about is a test that only proves my code does what my code does.
The honest position isn't “yes” or “no.” It's “yes, for the mechanics; no, for the judgment.” Use it to remove tedium. Keep your hands on the part that decides whether the software is correct. Outsource the typing, never the thinking.
Frequently asked questions
Can AI-written tests catch real bugs?
They can, but mostly bugs introduced after the test exists: regressions. A test written by reading buggy code will usually assert the bug as correct. The safest pattern is writing tests before the implementation, so the expected values come from intent.
Is high test coverage from AI a good sign?
Not on its own. Coverage measures lines executed, not behavior verified. AI can push coverage up while every assertion is derived from the implementation, which means the suite can never disagree with the code. Read the assertions, not the percentage.
Should I use AI for unit tests or end-to-end tests?
I trust it most for boilerplate-heavy unit and component tests, where structure dominates. For end-to-end and integration tests, where the assertions encode real product behavior, I write the critical checks myself and let AI handle setup and scaffolding around them.
What's the one habit that makes AI testing safe?
Never let the AI derive the expected value from the code under test. Source it from the spec, the ticket, or your own reasoning. Better still, write the test first. The whole risk lives in where the “expected” value comes from.
About the author: I'm Andy Liu, a frontend engineer who uses AI coding assistants daily on production work. Everything here is from hands-on use, not press releases. Disagree with my take? Tell me on the Contact page.
Related: AI Coding Agents vs Chatbots: When to Use Which · How to Get Pixel-Perfect UI From Claude Code
留言
張貼留言