Green Doesn't Mean Right: Testing Code the Model Also Tested
Our test coverage went up about fifteen points in a year and I did not feel fifteen points safer. That gap bothered me for a while before I could name it. The number was real — the tests existed, they ran, they passed, they touched lines that used to be untouched. What had changed underneath the number was where the tests came from. They used to come from a person deciding what the code was supposed to do. Now a lot of them come from a model reading what the code already does and writing that down as an assertion. Those two artifacts look identical in the file and in the coverage report. They are not remotely the same thing.
A test is a claim about intended behaviour. It says: this system should do this, and if it ever stops, someone should be woken up. The whole value comes from the claim being independent of the implementation — from someone having decided the "should" separately, so that when the implementation drifts, the two disagree and the disagreement is loud. A model asked to write tests for an existing function does not have access to the "should." It has access to the "does." It reads the function, infers behaviour, and asserts that behaviour. The output is fluent, well-organised, thorough-looking, and, in the strict sense, a tautology: this code does what this code does. It will pass forever, right up until someone fixes a bug, at which point it will fail and be dutifully updated to match the fix.
Three ways a generated test asserts nothing
The first is the tautology above, and it is the common one. It's easy to spot once you're looking for it and nearly invisible when you're not, because the test reads like a specification — descriptive name, arrange-act-assert, tidy. Read the assertion and ask where the expected value came from. If the answer is "from running the code," you're looking at a mirror.
The second is worse: the test pins in a bug. A function is off by one, or rounds the wrong way, or returns an empty list where it should return null. The model observes the actual output and writes expect(result).toBe(9). The defect is now protected. Fixing it turns the suite red, and a red suite is friction, and friction is how a wrong answer gets a defender. We found one of these in a currency-rounding path — a half-cent behaviour that was quietly incorrect, load-bearing in a test, and therefore the thing an engineer had to argue against rather than simply fix.
The third is the mock that asserts the mock. Generated tests are extremely willing to stub out every dependency, which makes them fast and hermetic and makes the failure mode invisible. When the setup teaches the mock what to return and the assertion checks that the mock returned it, the test exercises nothing but the framework. Coverage lights up. The system under test is untouched.
Coverage measures which lines ran. It has never measured whether anyone decided what those lines should do. That distinction was academic when tests were expensive to write. It stopped being academic when they became free.
What we changed
The first rule is about sequence, not tooling: the expected values have to exist before the implementation is read. In practice that means the pull request — or the design doc, for anything larger — carries a short list of behavioural claims in plain English before any code is generated. Three to six lines: what this must do, what it must refuse to do, what it must do when the input is hostile or the dependency is down. Generate all the test code you want from that list. The list is the human part, and it is the only part a model structurally cannot supply, because it is a statement about what we want rather than an observation of what exists.
The second is a cheap empirical check that has done more for us than any amount of arguing: break the code on purpose and see whether anything goes red. Flip a comparison, drop a branch, return the wrong default. If the suite stays green, the tests covering that code test nothing, and you have learned it in thirty seconds. This is mutation testing wearing work clothes — we run a real mutation tool on two core modules nightly, but the manual version is available to everyone in every review and costs nothing. Reviewers now do it on any PR where a large block of tests arrived alongside a large block of implementation.
The third is that we stopped treating coverage percentage as a quality gate. It never measured what we pretended it measured, and now that generating covering tests is trivial it's actively misleading — an easy number to move without moving the underlying thing. We still watch it, because a sharp drop means someone deleted safety, but it no longer blocks a merge. What blocks a merge is the behavioural claim list and a reviewer who has satisfied themselves that at least one test would fail if the feature broke in the way it is most likely to break.
Why I think this is a standard and not a preference
Every one of these problems is old. Tautological tests, bug-pinning assertions, and mocks that test themselves all existed when humans wrote everything — I've written all three. What changed is the rate and the disguise. They used to be produced slowly, by a tired person, and they looked tired: sparse, awkward, obviously thin. Now they arrive in volume, instantly, and they look like the best tests anyone on the team has ever written. Fluency is doing the same work here that it does everywhere else in AI-assisted engineering — it removes the surface signals we used to use to decide where to look harder. So the check has to become deliberate, because it will no longer happen by accident.
The underlying trade is the same one that keeps showing up: the model made the artifact cheap and left the judgment exactly where it was. A generated test suite is genuinely valuable — it's faster than we were, more consistent, and better at the boring exhaustive cases people skip. It just cannot tell you whether the system is right, because it has no access to right. Somebody on the team has to hold the "should," write it down before the code exists, and be willing to say the suite is green and we still don't know. That person is not going to be the model. That is the job.
If your coverage number went up and your confidence didn't, I'd be glad to compare notes. Reach me at arun@arunjalota.com.