Mocking LLM Calls
The most powerful feature of Sage’s testing framework is first-class LLM mocking. In test files, you can specify exactly what infer calls should return, making your tests deterministic and fast.
Basic Mocking
Use mock infer -> value; to specify what the next infer call should return:
test "infer returns mocked value" {
mock infer -> "This is a mocked response";
let result: String = try infer("Summarise something");
assert_eq(result, "This is a mocked response");
}
The mock is consumed by the infer call — each mock is used exactly once.
Multiple Mocks
When your test makes multiple infer calls, queue up multiple mocks in order:
test "multiple infer calls" {
mock infer -> "First response";
mock infer -> "Second response";
mock infer -> "Third response";
let r1 = try infer("Query 1");
let r2 = try infer("Query 2");
let r3 = try infer("Query 3");
assert_eq(r1, "First response");
assert_eq(r2, "Second response");
assert_eq(r3, "Third response");
}
Mocks are consumed in FIFO order (first in, first out).
Mocking Structured Output
For typed infer calls, mock with the appropriate record structure:
record Summary {
text: String,
confidence: Float,
}
test "structured infer returns typed mock" {
mock infer -> Summary {
text: "Quantum computing is fast.",
confidence: 0.88
};
let summary: Summary = try infer("Summarise quantum computing");
assert_eq(summary.text, "Quantum computing is fast.");
assert_gt(summary.confidence, 0.8);
}
Mocking Failures
Use fail("message") to mock an infer failure:
test "agent handles infer failure" {
mock infer -> fail("rate limit exceeded");
let handle = spawn ResilientResearcher { topic: "test" };
let result = await handle;
// Agent's fallback behaviour
assert_eq(result, "unavailable");
}
This is essential for testing error handling paths.
Testing Agents with Mocks
When testing agents that use infer, mocks are consumed by the agent’s infer calls:
agent Researcher {
topic: String
on start {
let summary = try infer("Research: {self.topic}");
emit(summary);
}
on error(e) {
emit("Research failed");
}
}
test "researcher emits summary" {
mock infer -> "Quantum computing uses qubits.";
let result = await spawn Researcher { topic: "quantum" };
assert_eq(result, "Quantum computing uses qubits.");
}
Testing Multi-Agent Systems
For agents that spawn other agents, each agent’s infer calls consume mocks in execution order:
test "coordinator gets results from two researchers" {
mock infer -> "Summary about AI";
mock infer -> "Summary about robots";
let c = spawn Coordinator {
topics: ["AI", "robots"]
};
let results = await c;
assert_contains(results, "AI");
assert_contains(results, "robots");
}
Mock Queue Exhaustion
If an infer call is made without an available mock, the test fails with error code E054:
Error: infer called with no mock available (E054)
Always provide enough mocks for all infer calls in your test.
Best Practices
- One assertion per test — easier to identify failures
- Descriptive mock values — make it clear what’s being tested
- Test error paths — use
fail()to test error handling - Keep mocks simple — avoid complex JSON in mocks when possible