The first time we pointed a model at an untested module, it wrote 140 tests in a couple of minutes. Coverage went from 20% to 85%. The product was no safer, and we spent a week understanding why.
The tests asserted the implementation
Generated tests describe what the code does, not what it should do. Run them against a bug and they pass, because they were written from the buggy code. Then they fail on every legitimate refactor, which teaches the team to delete tests.
What we do instead
- Write the behavioural assertions by hand, from the acceptance criteria, before implementation.
- Use the model to expand inputs: boundaries, malformed payloads, unusual orderings, unicode, time zones.
- Use it to generate fixtures and factories, which is tedious work with no judgement in it.
- Keep a small suite of end-to-end tests that a human wrote and can explain.
The coverage trap
Coverage measures execution, not verification. The metric we watch instead is whether the suite fails when we deliberately break a rule — a mutation check, run occasionally rather than continuously, on the modules that matter.
A test you would not have thought to write is valuable. A test that only restates the code is a liability with a green tick.
Where models genuinely help in QA
Triaging flaky failures, summarising a session into reproducible steps, generating realistic synthetic data that respects the schema's constraints, and reviewing a diff for missing error paths. All of those are real time savings and none of them require trusting the output blindly.
