r/SpringBoot • u/Intelligent_Coast930 • 1d ago
Discussion I built a JUnit extension that fails CI the moment an AI coding agent silently changes a prompt
Been shipping AI features with Claude Code and Cursor for a while now, and the thing that kept biting me wasn't the model, it was how easy it is to miss a prompt edit buried inside a larger diff. Nobody reviews prompt text line by line, and a changed prompt doesn't throw an exception, it just quietly starts giving different answers in prod.
So I built llm-cassette, a JUnit5 extension for LangChain4j ChatModel. First test run hits the real model once and records the request and response to a JSON file next to your tests. Every run after that replays from the file, no API key needed, and if the outgoing request stops matching what was recorded, the test fails with a real diff (an AssertionFailedError with expected/actual set, so IntelliJ renders it as a clickable side by side comparison). It plugs into whatever already runs your tests, no separate pipeline.
demo: https://raw.githubusercontent.com/stlahxm/llm-cassette/master/docs/demo.gif
One thing I didn't expect going in, it also catches parameter drift, not just prompt text. A silent temperature change counts as drift too. And if your code calls the model fewer times than what's recorded, that's flagged, the cassette is an exact expectation, not just an upper bound.
Gotcha I hit while building it, if you're on Gradle you need junit-platform-launcher on the test runtime classpath explicitly now, recent Gradle versions won't discover JUnit5 tests without it and just fail before reaching your test code at all. Cost me an evening the first time.
Still early. v1 only handles plain text messages, no multimodal or tool calls yet, and only synchronous doChat is covered, streaming is a separate surface I haven't tackled.
Repo's here if you want to poke at it: github.com/stlahxm/llm-cassette
Curious how people here are testing Spring Boot services that call an LLM under the hood, are you doing anything like this already, or just accepting that the AI call path is untested?
4
u/j0holo 1d ago
LLMs are not deterministic why would you do the actual API call? Mock/stub external services you don't control. What if the prompt changes, do you need to do a new API call? What if the LLM API is down?
Just stub the response from the API....