Most model-dependent suites fail in one of two directions. Either every test calls the real model, so CI is slow, metered, and flaky, or every test mocks the model so aggressively that the suite verifies nothing. The first problem is well documented: an author building an agent that runs in CI notes that unit tests calling a real LLM are slow, cost money on every run, flake when the provider has a bad day, and return different output for the same input (Building an AI Agent You Can Test in CI).
The second problem is subtler. A mock that returns a placeholder string proves the plumbing runs, not that it runs correctly. The fix in that same article is a deterministic fake at every external boundary: a rule-based fake whose complete() pulls the relevant sentence from a CONTEXT: block in the prompt, and whose tool_call() selects a tool by matching the user message against tool names and descriptions, then fabricates schema-valid arguments from each tool's JSON schema.
The split by tier is not unique to AI. Suites have long separated fast unit tests from slower integration tests, with the standard advice to run unit tests on every build and integration tests on a longer cadence (How to keep Unit tests and Integrations tests separate in pytest). A directory boundary or a marker group, not a naming convention someone has to remember, is what makes the model suite tractable.
The vocabulary matters too. A stub is a fixture that returns canned data; a mock is a behavior expectation, and the distinction is debated (What's the difference between a mock & stub?). Practically: tier 1 tests are stubs, and tier 2 tests are where you check behavior, such as whether the client sent the right auth header or retried once on a 429.
RAG makes this harder still, because output quality resists pass or fail assertions. A single chunking change can fix one edge case and quietly degrade others, and a shared staging index can be contaminated by another developer's experiment, so retrieval results mix (RAG CI/CD: Test and Gate AI Apps in Preview Environments). That is why tier 3 is a separate, scheduled, capped tier rather than a gate on every pull request.



