Why Agent Tests Are Different
Testing a traditional API endpoint is straightforward: you send a fixed input and assert on a deterministic output. Agent testing breaks both of those assumptions. The model's output varies between runs even for identical inputs. The agent's behavior is emergent, it depends on the interaction between the prompt, the model's weights, and the tools available. You cannot write assertEqual(agent.run(task), expected_output) and expect it to pass reliably.
This doesn't mean agent testing is impossible, it means you need a different mental model. Instead of testing exact outputs, test behaviors. Instead of testing deterministic sequences, test invariants. Instead of asserting on the LLM's reasoning, assert on the structural properties of what it produces.
The Three Sources of Agent Non-Determinism
- Sampling temperature: Even at temperature=0, many models are not guaranteed to produce identical outputs on repeated calls due to hardware floating-point differences and batching effects.
- Model updates: Provider model updates (even patch versions) can change behavior. A test passing today may fail next week because the model's behavior shifted.
- Tool output variability: Real-world tools return different data over time. A web search from yesterday returns different results today.
The Agent Testing Pyramid
The classic testing pyramid applies to agents, but the layers have agent-specific meanings. A well-structured agent test suite invests most heavily in unit tests at the base, uses an eval harness for semantic correctness, and reserves expensive integration and E2E tests for critical flows.
Layer 1, Unit Tests (40% of test effort)
Unit tests in an agent context test components that don't require a live LLM call. Tool schemas, parameter parsers, output formatters, budget managers, context assemblers, and prompt template renderers all have deterministic logic that can be tested conventionally. These should make up the majority of your test suite: they're fast (milliseconds), cheap (no API calls), and stable (no non-determinism).
Layer 2, Eval Harness (30% of test effort)
The eval harness runs real LLM calls against a curated set of tasks and scores the outputs using automated metrics: semantic similarity to golden answers, entity recall, tool selection correctness, and format compliance. This is where you catch behavioral regressions caused by prompt changes or model updates. Run on every PR, but cache LLM responses to control cost.
Layer 3, Integration Tests (20% of test effort)
Integration tests run the full agent against real tools (or high-fidelity sandboxed versions). They verify that the agent correctly orchestrates multi-tool workflows, handles tool errors, and produces outputs that satisfy end-to-end task requirements. These run nightly or on release candidates, not on every commit.
Layer 4, End-to-End / Human Eval (10% of test effort)
Full task runs with human reviewers or a strong LLM judge scoring output quality on a multi-criteria rubric. Expensive, slow, but the only way to catch subtle quality degradations that automated metrics miss. Run before major releases and when the eval harness shows ambiguous results.
What to Unit Test in an Agent
Many teams skip unit testing for agents because 'the interesting behavior comes from the LLM.' This is a mistake. A large fraction of agent failures come from deterministic code around the LLM: parsing tool calls, assembling context, enforcing budgets, routing decisions, output formatting.
High-Value Unit Test Targets
- Tool schema validation: Every tool's JSON schema should be tested with valid inputs, invalid inputs, and boundary values. A schema that rejects valid parameters silently fails tasks.
- Prompt template rendering: Variables injected into prompt templates (task description, user name, context snippets) should produce exactly the right output. Test all template variables with representative values and edge cases (None, empty string, very long strings).
- Context assembler: Given a fixed set of messages and a token budget, verify the assembler produces a context of exactly the right size with the correct priority ordering.
- Tool router: If your agent uses a router to decide which tool to call, test the routing logic with representative queries. Verify each routing rule selects the right tool.
- Output parser: Parse tool call JSON from model output. Test with well-formed JSON, malformed JSON, truncated JSON, and JSON with extra fields.
- Budget manager: Token counting and budget zone classification are deterministic. Test that zone thresholds trigger at exactly the right utilization percentages.
Handling Non-Determinism
When you can't avoid testing against the LLM directly, you need strategies for making those tests meaningful despite non-determinism. There are four primary approaches, each suited to different test scenarios.
Strategy 1, Semantic Equivalence
Rather than asserting the exact output text, embed the model's output and compare its cosine similarity to the golden answer embedding. Set a threshold (typically 0.85–0.92) above which the test passes. This tolerates paraphrasing while catching responses that are semantically wrong. Run the test 3–5 times and average the similarity scores to reduce sensitivity to a single unlucky sample.
Strategy 2, Behavioral Contracts
Define what the output must contain or do, not what it must say. For a customer support agent: the response must include the order number (entity assertion), must not include competitor names (negative assertion), and must call the check_order tool exactly once (structural assertion). These contracts are stable across model updates because they test behaviors, not phrasing.
Strategy 3, Temperature Pinning
Set temperature=0 for all test runs. Most models are more stable at temperature=0, though not perfectly deterministic due to hardware factors. Temperature pinning reduces, but does not eliminate, output variance. Combine with retry logic: if a test fails once at temperature=0, re-run it twice before marking it as a failure.
Tool Call Testing Strategies
Tool calls are the primary side-effectful operations an agent performs. Testing them requires distinguishing between what you're actually trying to test: the agent's decision to call a tool vs the tool's implementation vs the agent's use of the tool's result.
What Mock Testing Gets Right
Mock testing is correct for verifying that the agent calls the right tool with the right parameters when given a specific task. You don't need the real API to test that 'when asked for a product price, the agent calls get_price(product_id=X)'. The decision logic is in the model and prompt, which you can test in isolation from the live tool.
What Mock Testing Gets Wrong
Mock testing misses failure modes that only appear with real tools: authentication errors, rate limit handling, unexpected response schema changes, network timeouts, and empty result sets. These failure modes cause agent misbehavior in production that mock tests never surface. Dedicate a portion of your integration test suite to real tool calls specifically to catch these.
Testing Error Handling
Error handling is an often-neglected dimension of tool call testing. What does the agent do when a tool returns an error? What if the tool times out? What if the result is empty? Build explicit test cases for each error type your tools can produce. Inject these errors into mock responses and verify the agent responds gracefully, retries where appropriate, falls back to alternatives, or surfaces a clear error to the user.
Prompt-Level Unit Tests
Individual prompt components can be tested in isolation before composing them into the full system prompt. Prompt-level testing catches errors in individual capability instructions before they interact with other components and produce confusing emergent failures.
Capability Isolation Tests
Test each section of your system prompt independently. The tool use section: does the model call tools when instructed to? The refusal policy: does the model refuse prohibited requests? The output format instructions: does the model produce the specified JSON structure? Isolation makes it easy to identify which prompt section is responsible when behavior breaks.
Prompt Section Interaction Tests
After testing sections in isolation, test their interactions. Two individually correct prompt sections can conflict with each other. For example, 'be concise' and 'always include a bulleted summary' may produce inconsistent behavior when both are active. Interaction tests catch these emergent conflicts before they reach users.
Behavioral Contracts Over Exact Match
The highest-leverage shift in agent test philosophy is moving from exact-match assertions to behavioral contract assertions. This shift makes tests significantly more stable, more meaningful, and easier to maintain.
Defining Behavioral Contracts
- Structural contracts: Output must be valid JSON. Response must contain sections [introduction, analysis, recommendation]. Tool must be called before response is generated.
- Positive content contracts: Response must mention the customer's name. Answer must include a date. Recommendation must reference the user's stated constraint.
- Negative content contracts: Response must not mention competitors. Output must not contain PII. Answer must not contradict the data in the tool result.
- Behavioral sequence contracts: Agent must call search before summarize. Agent must not call delete without prior confirmation. Agent must complete all sub-tasks before final report.
- Quantitative contracts: Response must be between 50 and 500 words. Agent must not make more than 5 tool calls per task. Latency must be under 10 seconds.
Test Data and Fixture Management
Agent test suites need well-managed test data: task inputs, golden outputs, tool response fixtures, and adversarial inputs. Poor test data management is one of the most common reasons agent test suites become unreliable over time.
Golden Output Management
Golden outputs should be stored in version control alongside the code. When you deliberately change agent behavior, update the golden outputs as part of the same PR. This makes behavioral changes visible in code review. Commit semantically meaningful golden outputs, not arbitrary responses, but responses that represent the ideal answer to the test task.
Tool Response Fixtures
For tests that use mock tools, capture and store real tool responses as fixtures. When the real tool returns a new response shape (due to an API update), update the fixtures. This keeps mock behavior aligned with reality. Tag fixtures with the date they were captured and refresh them quarterly or when the tool API updates.
Task Bank Construction
Build your task bank from three sources: representative production tasks (sampled and anonymized), known failure cases (tasks that caused bugs or user complaints), and hand-crafted edge cases (empty inputs, adversarial inputs, boundary conditions). A task bank with all three categories catches a much wider range of regressions than one built from only one source.
Integrating Agent Tests Into CI
Agent tests have different CI requirements than conventional tests. LLM calls are slow and expensive; you can't run your full eval suite on every commit. Structure your CI pipeline in tiers that match speed and cost to the gate.
CI Test Tiers
- Pre-commit (< 5 seconds): Unit tests only, no LLM calls. Schema validation, prompt rendering, parser tests. Run on every commit push.
- PR gate (2–5 minutes): Cached eval harness. Use response caching to avoid re-running LLM calls for unchanged prompt sections. Run on every PR.
- Nightly (10–30 minutes): Full eval harness without caching. Integration tests with real tools. Run every night on main branch.
- Release gate (1–4 hours): Full integration test suite + E2E tests + adversarial scenarios. Run before every production release.
Common Testing Pitfalls
Pitfall 1, Over-Mocking
When every component is mocked, you're testing the mocks, not the agent. If your LLM is mocked, your tool is mocked, and your database is mocked, a passing test proves nothing about whether your agent actually works. Preserve at least one integration test layer where real components run together.
Pitfall 2, Testing Only the Happy Path
Most agent test suites test the happy path: the task is clear, tools succeed on the first call, the model produces a well-formed response. Production failures cluster in edge cases: ambiguous tasks, tool errors, partial results, context overflow, malformed user input. Explicitly include these in your task bank.
Pitfall 3, Not Versioning Golden Outputs
If your golden outputs aren't in version control, you'll find yourself manually updating them whenever you change a prompt and losing track of whether the change was intentional. Treat golden outputs as code, version them, review changes in PRs, and require explicit sign-off when they change significantly.
Pitfall 4, Ignoring Flakiness
A test that passes 80% of the time is not a test, it's noise. Flaky agent tests are usually caused by over-tight similarity thresholds, tests that depend on live internet state, or tests with no retry logic. Fix flaky tests immediately; don't let them accumulate and erode confidence in the entire test suite.
Frequently Asked Questions
Build secure agentic systems with AgentixForce
We help companies design, build, and secure production-grade AI agent systems. From architecture reviews to full implementation, our team brings deep expertise to every engagement.