Why AI-generated tests can pass with the wrong code
Run a booking example where tests miss a deadline bug. Add the missing boundary check and see what coverage and mutation testing tell you.
AI-generated tests can miss a mistake or repeat the implementation's wrong assumption. A passing run establishes that the selected checks passed, not that they represent the whole requirement. Check the expected results and the cases that would distinguish correct code from a plausible mistake.
A booking can be cancelled before 18:00. Tests at 17:00 and 19:00 pass even when the code incorrectly accepts 18:00 itself. The missing test is not another ordinary booking: it is the exact deadline.
Two green tests accompany a proposed booking change. Before approving it, compare their inputs with the rule they are meant to check. This illustrative example is deliberately constructed; it is not a recorded AI failure or a measurement of any model.
Would you accept this change?#
The agreed rule is precise: cancellation is allowed before 18:00. For this small exercise, input is an already-validated integer number of minutes after midnight on one agreed local day. Dates, time zones and permission checks are outside the function. Compare the correct implementation with a plausible one-character mistake.
The tests below check an allowed time and a rejected time. Their default selection is the correct function. The demonstration runner switches rule to can_cancel_buggy without changing either assertion.
Would you accept this change?
Both tests pass against the wrong implementation. Which additional input would make the two implementations disagree, and what result does the requirement demand?
Reveal the missing case
At exactly 18:00, cancellation must be rejected. The correct function returns false; the buggy function returns true. Another test at 16:00 would not distinguish them.
Try it: move the cancellation time
The agreed rule allows cancellation before 18:00. Change the input and compare the correct function with the deliberate <= mistake.
Illustrative deterministic functions; this control does not execute the downloadable Python fixture. Run that file for the actual test results.
Choose the expected result before running the code#
Write the expected result from the agreed rule: exactly 18:00 is too late. Do not run the new implementation, observe true and copy true into the expectation. That would record what this version does while leaving the requirement unchecked.
The stronger suite retains the first two cases and checks the boundary and its neighbouring minutes. The example uses minutes, so these are its adjacent inputs; a system accepting seconds would need cases at that precision.
The exact-deadline assertion fails against the buggy function. This failure is useful because it names a disagreement with an already-decided rule. A failure caused by a missing Python interpreter or an unavailable database would not establish that disagreement.
There are occasions when recording existing output is exactly the job. Characterization tests preserve observed behaviour while refactoring. They answer a different question from whether a newly implemented rule is correct. State which question a test answers before judging its expectation.
Try a deliberate mistake#
Start with the correct function, then deliberately allow equality at the deadline. Do the tests complain? That is the central experiment behind mutation testing: introduce a small code change and check whether tests detect it. PIT’s documentation explains detected and surviving mutations, as well as mutations that do not change relevant behaviour.
Our demonstration manually selects one changed Python function. It does not run PIT, search a whole program for mutations or produce a mutation score. It establishes one narrower result: the weak suite misses this particular change and the boundary suite detects it.
Both weak tests execute the return expression. Merely knowing that the line ran would therefore miss the problem. Coverage asks which code was exercised; the assertion asks whether an observed result matches an expectation; reviewing the requirement asks whether that expectation is right. PIT’s introduction distinguishes coverage from the ability to detect introduced faults.
Even detecting every mutation tried would not settle an omitted feature. Our function has no caller identity, so no variation of its deadline comparison establishes who may cancel a booking. Add the relevant requirement and test at the appropriate boundary rather than treating one successful experiment as a complete review.
Run the passing and failing versions#
Save the complete Python example and run it from that folder with Python 3.11 or later and its standard sqlite3 module. No package installation, network connection or disk database is needed. The displayed Python fragments come from that same file.
python3 booking_tests.py –demo weak runs two checks against the buggy rule. Both pass; the process exits with code 0.
python3 booking_tests.py –demo boundary runs five checks against the same buggy rule. The exact-deadline check fails; the process exits with code 1. This red result is intentional.
python3 booking_tests.py selects the corrected versions across all three exercises. All 11 checks pass. The example notes explain the other modes and their expected results. These commands were executed with Python 3.13.5; the counts describe this fixture, not the website’s test suite.
Review the evidence, not the test count#
For a generated change, ask the author to connect each important rule to an observable check. Inspect where its expected value came from and name a plausible wrong implementation that the check would reject. The deadline example makes that inspection concrete: a count of tests is less informative than seeing whether exactly 18:00 was tried.
Use unit and integration tests to choose which components must participate in that check. Separately, a golden set evaluates the agent’s task completion. An agent can submit passing application tests and still fail an independently retained task expectation. Keep those expectations reviewable rather than deriving them from the submitted implementation. The same check applies when you review the whole pull request: read the tests last, as evidence rather than proof.
IF YOU REMEMBER ONE THING
Find an input that separates the intended behaviour from a believable mistake. Write its expected result from the requirement, then confirm the test rejects the mistake.
Questions people also ask
3 QUESTIONSShould a different AI agent write the tests?
A separate reviewer can propose different cases, but a separate conversation is not evidence of independent expectations. Both agents may make the same interpretation. Give the reviewer the agreed behaviour and ask which observation would expose a wrong implementation.
Does a surviving mutation always mean a missing test?
No. Inspect it first. The changed code may behave identically for all relevant inputs, or the difference may be outside the behaviour you intend to test. In this example the changed deadline is meaningful because the stated rule distinguishes exactly 18:00.
Is this a benchmark of AI testing tools?
No. The functions and test cases are original teaching examples. No model was evaluated and no failure rate or tool ranking is inferred from them.