The job of most CI tests around an embedding pipeline is plumbing, not linguistics. You want to know that your client batches 96 inputs into chunks the API accepts, that a 429 triggers exponential backoff, that a truncated response is retried, that the cache key includes the model name, and that an empty document never reaches the network. None of those facts depend on what the model actually thinks. A stub built from a hash is deterministic: the same string yields the same vector on every machine, forever, which is exactly the property a flaky metered API destroys. One practitioner's write-up of an offline agent test harness describes this as "deterministic fakes at every external boundary," using a local hashed bag-of-words embedder so the full stack runs with zero accounts or API keys the AI Integration Sandbox write-up. That is the same trick used for anonymization: deterministic hashing with a fixed salt keeps outputs repeatable and reversible-to-the-same-input, which is why hash-derived transforms are common in data masking pipelines Xata on anonymization.
The distinction matters for language, too. A stub is a fixture that returns canned data; a mock is an expectation about behavior Stack Overflow on mock vs stub. A hash embedder is a stub. Do not let it turn into a mock that asserts "the model was called with these exact strings," because then your test is a transcript of your current implementation and will break on every refactor.
The broader dev/prod parity debate is relevant here. Teams that mocked every dependency and integrated for the first time after years of development found the real services much slower than the mocks, and the integration failed badly dev/prod parity and microservices. The lesson is not "never stub." It is "stub the shape, then verify the real thing on a schedule." Preview environments and staging exist to catch exactly the discrepancies that a local fake cannot Statsig on staging environments.



