Skip to content

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(())
}

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.

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 twice

MockTurn::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.

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."));

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);

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.