Never Trust AI-Written Tests To Validate AI-Written Code
The tests pass. The code is still wrong. Here is the trap: your AI assistant wrote the code with a wrong assumption — then wrote the tests with the same wrong assumption. They agree with each other, and they are both wrong. A green suite proves consistency, not correctness.
Why one mind can't grade its own homework
Unit tests work as a safety net because they encode a second, independent opinion about what the code should do. When a human writes tests for their own code, that independence is already weakened — which is why code review and QA exist. When a single LLM session writes both sides, independence collapses entirely: the misreading of the spec that produced the bug is sitting right there in context, shaping every assertion the model writes next.
This isn't hypothetical. An empirical study of LLM test-generation workflows found that tests generated after faulty code detect only 14% of faults, versus 25% for tests generated independently of the implementation — the errors propagate, and "incorrect implementations and tests are mutually consistent, masking defects rather than revealing them." A second study measuring the influence of incorrect code on test generation found tests generated for incorrect code suffer a 47% worse bug-detection rate on repository-level examples.
The failure modes to watch for
- Inverted conditions. The model flips
>=to>in the code, then asserts the flipped behavior in the test. Both agree; the boundary bug ships. - Swapped arguments.
transfer(to, from)instead oftransfer(from, to)— and the test constructs its fixture in the same swapped order. - Snapshot laundering. The model runs the buggy code, observes the wrong output, and asserts that output as the expected value. The test is a mirror, not a judge.
- Vacuous assertions. Tests that check "no exception was raised" or assert on a mock the test itself configured — 100% pass rate, near-zero verification.
Rules of thumb
| Situation | What to do |
|---|---|
| AI wrote code + tests together | Check the code against each stated invariant from the spec, not against its own tests |
| AI wrote the tests | Read every assert line; verify expected values by hand or from the spec, never from program output |
| You need tests for AI code | Generate tests from the requirements in a fresh session, without the implementation in context |
| Suspicious green suite | Reproduce the behavior yourself with one manual run of the real thing |
| High-stakes logic | Add mutation testing or property-based tests as an independent referee |
The unifying rule: the oracle — the source of "what's correct" — must come from outside the model run that produced the code. That's the spec, your own reproduction, or a mechanically independent check.
Power-user moves: mechanical independence
- Mutation testing. Tools like Stryker (JS/TS, C#, Scala) automatically inject small bugs (mutants) into your code and re-run the suite. If a mutant survives — tests still pass — you've found exactly the kind of weak, self-agreeing test AI tends to write. A surviving-mutant report is a to-do list of assertions to strengthen.
- Property-based testing. With a library like Hypothesis (Python), you state invariants ("decode(encode(x)) == x", "output is always sorted") and the framework generates hundreds of adversarial inputs. Properties come from the spec, not from the implementation, so they can't inherit its assumptions.
- Split the roles. If you want AI-written tests at all, generate them in a separate session from the natural-language requirements only. The incorrect-code study found that adding a natural-language description alongside code improved bug detection by +34% — description-driven tests are anchored to intent, not to the buggy code.
- Make the test fail first. Before trusting any new test, deliberately break the code it covers and confirm the test goes red. A test you've never seen fail has proven nothing.
The bottom line
Never let AI tests be the only judge of AI code. Passing tests written by the same mind that wrote the bug is agreement, not evidence. Get your oracle from outside: the spec, your own hands, or a mutation-testing run that tries to kill the suite.
Resources
- On the risk of coding before testing: An empirical study on LLM-based test generation workflow (arXiv) — the 14% vs 25% fault-detection result
- Measuring the Influence of Incorrect Code on Test Generation (arXiv) — 47% worse bug detection on real repos; +34% from natural-language descriptions
- What is mutation testing? — Stryker Mutator docs
- Mutation testing in .NET — Microsoft Learn
- Hypothesis — property-based testing for Python
Building with AI coding assistants? Yeda AI designs, audits, and ships production LLM systems.