Picture a developer asking a coding assistant for a new discount rule. The assistant writes the function and, a minute later, six unit tests to go with it. All six pass, the build turns green, and the change ships.
Now suppose the function rounds the discount the wrong way. The tests probably round it the wrong way too, because they came from the same reading of the task. A passing run shows that the code and its tests agree with each other. It does not show that either one matches what the business asked for.
That gap sits at the center of quality engineering for AI-assisted development: the practice of designing tests, reviews, and release checks for teams whose code is partly written by tools such as Cursor, Claude Code, or GitHub Copilot. Its central job is making sure tests are judged against expectations that did not come from the same model, prompt, or session that produced the code.
Why Do AI-Generated Tests Pass When the Code Is Wrong?
Every test needs an expected answer, and testers call whatever supplies that answer the "test oracle." It might be a requirement, a domain expert or a known-good earlier version. Researchers refer to the difficulty of choosing it as the oracle problem.
When someone else writes tests from the spec, the spec is the oracle. When an assistant writes both the code and the tests from one prompt, its own reading of that prompt becomes the oracle for both. A misunderstanding doesn't get caught; it gets copied into two places.
Generated tests can also capture whatever the function returns and assert that exact value. That is a snapshot of current behavior rather than a check of intended behavior, so a wrong result gets locked in.
Volume makes this harder to spot. In a controlled experiment, developers using GitHub Copilot finished a single programming task about 56% faster. More code means more tests to review, while review time does not grow at the same pace. So the hard part of testing AI-generated code is that the usual independent review goes missing, and no one notices.
What Does It Cost When Tests Only Agree With the Code?
The first cost is false confidence: release decisions often lean on a pass rate, and self-confirming tests keep that number healthy while the protection behind it thins out.
The second is a suite that grows without getting stronger. Every generated test still has to run and be maintained, and as run times climb, teams may trim tests to hit a date, rarely by risk.
The third is security. In one study of Copilot suggestions, about 40% of the programs generated for security-relevant scenarios contained vulnerabilities. A separate user study found that people using an AI assistant wrote less secure code and were more likely to believe it was secure. Tests written in the same session may not look for the weakness the code missed.
At the delivery level, the 2024 DORA report found that higher AI adoption was associated with lower delivery stability. That is a correlation, not proof of cause, but it fits a pattern where AI-generated code quality rests on fewer real checks than the pass rate suggests.
How Can You Tell If AI-Generated Tests Are Checking Anything?
You rarely see this problem in a dashboard; you see it in the tests themselves.
- Assertions That Repeat The Implementation: The test recomputes the result with the same formula the code uses, or asserts a value copied from a test run.
- Tests And Behavior Changing In The Same Commit: If a change also edits the old assertions to match, nothing outside the change holds it to account.
- Mocks Tuned To The Happy Path: When every dependency returns exactly what the code expects, the test checks wiring, not outcomes.
- Coverage Rising While Escaped Defects Stay Flat: Coverage shows that code ran, not that anything checked it. One large study found coverage was not strongly correlated with fault detection once suite size was accounted for.
- Few Tests For Cases Nobody Wrote Down: Empty inputs, rounding, time zones, and permissions are easy to miss when tests follow the prompt.
For a quick check, take a function from a recent pull request, flip a comparison or change a constant, and rerun its tests. If they still pass, they were not really testing that line.
How Should You Test AI-Generated Code?
The goal is to give tests a source of truth that the code did not also come from, and then measure whether they catch real faults.
- Write the Expectation First: Capture acceptance criteria or example inputs and outputs, and generate tests from those. An assistant writing tests should get the requirement and public interface in a fresh session, not the implementation.
- Review Tests First: Start by asking where each expected value came from, because "the function returned it" is not an acceptable answer.
- Run Mutation Testing On Changed Code: A mutation testing tool plants small deliberate bugs, called mutants, and reruns your tests. A surviving mutant is a change no test detected. PIT for Java and Stryker for JavaScript and C# can limit runs to recently changed code.
- Measure Coverage Of The Diff: Tools like diff-cover report coverage for only the lines a pull request changed.
- Add Property-Based Tests Where Rules Are Clear: You state a rule that must always hold, such as "a discount never makes a price negative," and a library like Hypothesis generates many inputs to try to break it.
- Spend Human Attention By Risk: Payments, permissions, data deletion, and regulated workflows deserve human-checked expectations. A copy change can rely on generated tests.
What Should Engineering Leaders Measure Each Release?
Pass rates and repository-wide coverage say little about whether this week's changes were checked. Three measures focus on the changed code itself.
| Measure | What it tells you | Where it comes from |
| Diff coverage | Share of changed lines that ran during tests | Coverage report compared with the pull request diff |
| Mutation score on changed code | Share of planted bugs in changed code that the tests caught | Mutation testing scoped to recent changes |
| Changes with independent expectations | Share of changes whose tests trace to a requirement or human-written example | A pull request template field |
Watch the trend rather than any single value. A falling mutation score on changed code while coverage holds steady is a strong sign that tests are agreeing instead of checking. It also changes the release question from "did the suite pass?" to "how much of this change was tested, and against what?"
What Are the Limits and Tradeoffs?
None of these practices make a suite complete, and each one has a cost.
Mutation testing uses a lot of compute, and some "equivalent mutants" don't change behavior at all, so a person has to set them aside.
Independent expectations take time to write and are only as good as the requirements behind them. Property-based tests need well-chosen rules, or they pass without proving much. A second AI model reviewing the first may share its blind spots.
Human review capacity remains the hard limit. A realistic goal is not perfect coverage but knowing, for each release, which parts of the change were checked against an independent expectation and which were not.
Who should review AI-generated tests?
Ideally, someone who did not write the prompt that produced the code. Either way, the reviewer should start by asking where each expected value came from.
How QASource Approaches This
At QASource, quality assurance for AI-assisted development is part of the delivery model. Test cases and automation are generated on their internal platform from requirements, code diffs, and defect history. A QASource engineer then reviews every generated case and script for correctness and intent before it is committed to the client's repository.
Validation runs in the client's pipeline on every change, with suites partitioned by risk and coverage measured against the changed surface of each release. Each release decision is backed by a written finding that states what was validated, what wasn't, and the residual risk. A scoped assessment produces a generated-and-verified sample suite for one service and a coverage-against-change baseline.