Testing
A Rig agent takes any model, so in tests you can give it a mock model and
check your agent’s behaviour without calling a provider. Tests run offline, cost
nothing, and are deterministic. The mocks live in rig::test_utils, behind the
test-utils feature.
Each snippet below is the body of a test function:
#[tokio::test]async fn my_agent_test() -> anyhow::Result<()> { // snippet goes here Ok(())}Mock a model response
Section titled “Mock a model response”MockCompletionModel::text(...) returns a model that replies once with the
given text. Build a normal agent on top of it and prompt as usual; no API key
is needed.
use rig::test_utils::MockCompletionModel;
let model = MockCompletionModel::text("Hello from a mocked model!");let agent = AgentBuilder::new(model).preamble("You are friendly.").build();
let reply = agent.prompt("Hi").await?;
assert_eq!(reply.output(), "Hello from a mocked model!");Each scripted turn is used once. If the agent calls the model more often than you scripted, the extra call fails with a provider error, so a test can’t silently pass on a repeated answer.
Script a multi-turn tool loop
Section titled “Script a multi-turn tool loop”Real agents loop: the model asks for a tool, Rig runs it, and the model answers
with the result. Script each model turn with MockTurn: MockTurn::tool_call(id, name, args)
for a tool request and MockTurn::text(...) for the final answer. Clones of a
MockCompletionModel share their state, so keep one to inspect afterwards.
use rig::test_utils::{MockAddTool, MockCompletionModel, MockTurn};
let model = MockCompletionModel::from_turns([ // Turn 1: the model asks to call the `add` tool. MockTurn::tool_call("call_1", "add", json!({ "x": 2, "y": 3 })), // Turn 2: after seeing the tool result, it answers. MockTurn::text("2 + 3 = 5"),]);let probe = model.clone();
// Attach your real tool here; `MockAddTool` is a ready-made one.let agent = AgentBuilder::new(model).tool(MockAddTool).build();let answer = agent.prompt("What is 2 + 3?").max_turns(3).await?;
assert_eq!(answer.output(), "2 + 3 = 5");assert_eq!(probe.request_count(), 2); // the loop called the model twiceMockTurn::error(...) scripts a provider failure, which is how you test your
error handling and retries. For streaming agents, script the chunks with
MockCompletionModel::from_stream_turns and MockStreamEvent.
Assert what the model received
Section titled “Assert what the model received”The mock records every request it was sent. Use requests() to check the exact
request your agent built: system prompt, history, tools, and context documents.
use rig::test_utils::MockCompletionModel;
let model = MockCompletionModel::text("ok");let probe = model.clone();
let agent = AgentBuilder::new(model).preamble("You are a pirate.").build();agent.prompt("Ahoy").await?;
let sent = probe.requests();assert_eq!(sent.len(), 1);assert_eq!(sent[0].system_instructions(), Some("You are a pirate."));Test embeddings and RAG
Section titled “Test embeddings and RAG”MockEmbeddings::model() is an embedding model that returns the same fixed
vector for every text, so you can test the retrieval half of a RAG pipeline
without an embedding provider. sent_documents lists the context documents an
agent put into a request:
use rig::test_utils::{sent_documents, MockCompletionModel, MockEmbeddings};
let embeddings = EmbeddingsBuilder::new(MockEmbeddings::model()) .documents(["a green alien", "an ancient farming tool"])? .build() .await?;let index = InMemoryVectorStore::from_documents(embeddings).index(MockEmbeddings::model());
let model = MockCompletionModel::text("ok");let probe = model.clone();let agent = AgentBuilder::new(model).dynamic_context(2, index).build();agent.prompt("What is a flurbo?").await?;
// Both documents were retrieved and sent as context.assert_eq!(sent_documents(&probe.requests()[0]).len(), 2);Replaying recorded runs
Section titled “Replaying recorded runs”Mocks are best for the logic you own. To test against what a real model actually said, record a live run once and replay it in CI. See Recording & Replay.
