Snapshot and property testing for model output
Golden files, fuzzy matching, invariants and metamorphic relations — how to assert on text that is never byte-identical twice.
A test asserts that something is equal to something else. That instruction survives contact with a language model for about one afternoon: the same prompt, the same model and the same parameters return prose that is close in meaning and different in bytes, and a suite built on string equality starts failing for reasons that have nothing to do with your code. The usual reactions are both wrong. One is to delete the tests. The other is to keep them and re-approve every failure without reading it, which is the same thing with more ceremony.
The useful move is to change what you assert on. A schema holds when the wording does not. So does a tool name, a call count, a redaction, an ordering, a bound on length, and a relation between two runs that must hold whatever either one says. These pages work through that shift concretely — the snapshot machinery when a golden file is still the right tool, the mechanisms that make exact matching fail, and the properties and metamorphic relations that are testable precisely because the output is not.
Snapshot Testing LLM Output With Jest
How to write a Jest snapshot serializer that reduces a model response to the fields you meant to assert on, so the diff shows meaning rather than rewording.
9 min read
Snapshot Testing Python LLM Output With syrupy
Using syrupy's snapshot fixture, matchers and exclude filters on model responses, and the review workflow for approving a change on purpose.
9 min read
Approval Testing for Prompt Output
Approval testing names what golden-file suites for model output actually are: a record that a human read the new answer and signed it off.
8 min read
Why Exact-Match Snapshot Tests Fail on LLM Output
The five separate mechanisms that make the same request return different bytes, and why temperature zero does not remove any of them completely.
9 min read
Fuzzy Snapshot Matching for Non-Deterministic Text
Structural diffing, embedding similarity thresholds and key-fact extraction — what each technique can detect, and the specific failure each one is blind to.
9 min read
Updating Golden Files After an Intentional Prompt Change
A regeneration procedure that separates the change you meant from the regression you did not, instead of absorbing both in one commit.
9 min read
Reviewing a Snapshot Diff Before Approving It
A fixed order of checks for the two-minute decision on a golden-file diff, ordered so the changes that matter are seen before attention runs out.
8 min read
Snapshot Testing Structured JSON Output From a Model
Validate the schema on every run and snapshot the normalised values, so a diff shows a changed field rather than a reordered object.
9 min read
Storing Golden Files in Git Without Bloating the Repo
Why text goldens belong in Git and binaries do not, what Git LFS costs you in review, and when a manifest of content hashes is the better answer.
9 min read
Semantic Diff for Comparing Two Model Outputs in Tests
Four ways to score how far two generations differ in meaning, ordered cheapest first, and how to turn the score into an assertion you can defend.
10 min read
Property-Based Testing for LLM Output With Hypothesis
Generate the inputs with Hypothesis, keep the model as the system under test, and assert an invariant that holds across every generated case.
10 min read
Property-Based Testing LLM Output in JavaScript With fast-check
Building an async property around a model call in fast-check, and using the seed and path it prints to replay the exact failing case.
10 min read
Writing Invariants for LLM Output You Can Actually Test
Five families of property that hold for non-deterministic text, the ones that only look like properties, and how to derive yours from a spec you already have.
10 min read
Property-Based Testing a JSON Schema an LLM Must Satisfy
Two test directions around one schema: generate instances from it to harden your consumer, and generate prompt inputs to find where the model cannot satisfy it.
10 min read
Metamorphic Testing for LLM Prompts
How to test for correctness when there is no correct answer to compare against, by asserting a relation between the outputs of two related inputs.
10 min read
Testing That Paraphrased Inputs Get Consistent Answers
Implementing the paraphrase invariance relation end to end, including deciding what consistent means and separating paraphrase variance from sampling variance.
10 min read
Generating Test Inputs With a Grammar Instead of Examples
Writing a small context-free grammar for your inputs so a generator produces hundreds of realistic prompt variants, and keeping the derivation so failures are classifiable.
10 min read
Shrinking a Failing Property Test Case to Its Minimal Prompt
Why the minimal failing prompt is the deliverable rather than a nicety, how the two main shrinkers get there, and what non-determinism does to the process.
10 min read
Other topics
- LLM fundamentals & architecture
- Tokens, tokenization & context windows
- Prompt engineering
- Reasoning models & test-time compute
- Multimodal AI: vision, audio, video
- RAG & retrieval
- Embeddings & vector search
- AI agents & tool use
- Structured output & function calling
- Fine-tuning & post-training
- Local inference errors, string by string
- Running local models day to day
- Testing code that calls an LLM
- Regression suites for prompts
- Eval gates in CI
- Flaky tests against a model
- Determinism and the cost of testing
- Contract and streaming tests
- Testing tool calls and retrieval
- Inference, serving & latency
- Rolling out a prompt change
- Testing AI systems in practice
- Forecasting a time series
- Machine learning on tabular data
- Geospatial data and models
- Understanding audio that is not speech
- Understanding video
- Core computer vision tasks
- Machine learning on graphs
- Point clouds and 3D
- Evaluation, benchmarks & LLM-as-judge
- Sensor and IoT data
- Logs and event streams
- Models over biological sequences
- Machine learning on molecules
- Embedding and searching code
- Extracting invoices and purchase orders
- Receipts, statements and tax forms
- Insurance policies and contracts
- Deeds, court filings and patents
- Extracting from medical records
- Observability & LLMOps
- CVs, certificates and identity documents
- Shipping, customs and technical documents
- Meetings, email, chat and filled-in forms
- Building an extraction pipeline
- Business, property and inspection documents
- Contract clauses and insurance claims
- Regulated and compliance documents
- Consumer, travel and closing documents
- Mapping one chat API onto another
- SDK and framework migrations
- Hallucination & failure modes
- Re-embedding and model deprecation
- Cutting over between providers
- Parity gaps, shims and legacy endpoints
- Moving between model versions
- Migrating vector stores and caches
- Mapping capabilities and parameters
- Migrating pipelines and agents
- Contracts, runbooks and rollback
- Auditing a codebase before a cutover
- Compliance and fine-tune migration
- LLM cost engineering
- Routing, cost tracking and multi-tenancy
- What a migration does to your prompts
- AI security & prompt injection
- Privacy, compliance & data residency
- AI governance, policy & society
- Building reliable AI applications
- AI hardware, GPUs & compute
- Open-weight models & local inference
- AI for developers & coding agents
- AI in industry: vertical playbooks
- AGI, superintelligence, alignment & the long future
- Machine learning foundations
- NLP fundamentals & classical tasks
- Data engineering for AI
- Synthetic data & dataset curation
- AI product design & UX
- Search, ranking & recommendation
- Enterprise adoption & change management
- AI careers, skills & teams
- Reading AI research
- AI in science & discovery
- Robotics & embodied AI
- AI economics, markets & business models
- AI myths, hype & media literacy
- Context engineering
- Shipping AI features: patterns & anti-patterns
- Build it: end-to-end AI tutorials
- Python for AI: hands-on recipes
- TypeScript, React and the web
- Frameworks and SDKs
- Errors and troubleshooting
- AI facts, numbers and statistics
- The history of AI
- The maths behind AI
- Architectures beyond the transformer
- Reinforcement learning
- Diffusion and generative media
- Speech, audio and voice engineering
- Benchmarks, one at a time
- AI search visibility
- Infrastructure and operations
- Databases and storage for AI
- Knowledge graphs and structured knowledge
- Classical ML in production
- Regulation, jurisdiction by jurisdiction
- Prompt recipes and pattern library
- AI for people who do not write code
- Writing, media and creative work
- Edge and on-device AI
- Interpretability and model internals
- Field notes
- OpenAI model behaviour
- Claude model behaviour
- Gemini model behaviour
- Llama model behaviour
- Mistral model behaviour
- Qwen model behaviour
- DeepSeek model behaviour
- Cohere model behaviour
- Grok model behaviour
- Small model behaviour
- Hybrid model architectures
- Token cost by language and script
- Transliteration, romanization and script handling
- Locale-correct output
- Multilingual generation quality
- Multilingual pipelines
- The EU AI Act, article by article
- AI under the GDPR and EU data law
- US AI regulation, state and sector
- International AI governance and standards
- AI litigation and enforcement
- Running AI workloads on AWS
- Running AI workloads on Google Cloud
- Running AI workloads on Azure
- AI at the edge: Workers, Vercel and Netlify
- Serving models on Kubernetes
- Operating AI infrastructure
- Quantization formats and what they cost
- llama.cpp, flag by flag
- Ollama and the desktop local-model runtimes
- Local models on Apple Silicon
- Hardware for local inference
- Running speech and embedding models locally
- Model files, adapters and conversion
- VRAM arithmetic for local models