Testing
Because Rig models are just types that implement CompletionModel / EmbeddingModel, you can
swap in a mock and assert on your agent’s behaviour without ever calling a provider. Tests run
offline, cost nothing, and are fully deterministic. The mocks live in rig::test_utils (behind the
test-utils feature).
Mock a model response
Section titled “Mock a model response”MockCompletionModel::text(...) returns a model that always replies with the given text. Build a
normal agent on top of it and prompt as usual — no OPENAI_API_KEY required.
use rig::test_utils::MockCompletionModel;
#[tokio::test]async fn agent_returns_the_scripted_reply() { 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.unwrap();
assert_eq!(reply, "Hello from a mocked model!");}running 1 testtest agent_returns_the_scripted_reply ... okScript a multi-turn conversation
Section titled “Script a multi-turn conversation”Real agents loop: the model asks to call a tool, Rig runs it, then the model answers. Script each
turn with MockTurn — MockTurn::tool_call(...) for a tool request and MockTurn::text(...) for a
final answer. Because MockCompletionModel is Clone and shares its state, keep a handle to assert
how many times the model was invoked.
use rig::test_utils::{MockCompletionModel, MockTurn};use serde_json::json;
#[tokio::test]async fn agent_runs_the_tool_loop() { let model = MockCompletionModel::from_turns([ // Turn 1: the model asks to call the `add` tool. MockTurn::tool_call("call_1", "add", json!({ "a": 2, "b": 3 })), // Turn 2: after the tool result, it gives a final answer. MockTurn::text("2 + 3 = 5"), ]); let probe = model.clone();
let agent = AgentBuilder::new(model).preamble("Do maths.").build(); let answer = agent.prompt("What is 2 + 3?").await.unwrap();
assert!(answer.contains("5")); assert_eq!(probe.request_count(), 2); // the loop hit the model twice}Test embeddings and RAG
Section titled “Test embeddings and RAG”MockEmbeddingModel produces deterministic vectors, so you can test the retrieval half of a RAG
pipeline without an embeddings provider.
use rig::test_utils::{MockEmbeddingModel, MockTextDocument};
#[tokio::test]async fn builds_embeddings_offline() { let docs = [ MockTextDocument::new("doc-1", "a green alien"), MockTextDocument::new("doc-2", "an ancient farming tool"), ];
let embeddings = EmbeddingsBuilder::new(MockEmbeddingModel) .documents(docs) .unwrap() .build() .await .unwrap();
assert_eq!(embeddings.len(), 2);}Assert what the model received
Section titled “Assert what the model received”The mock records every request it was sent. Use requests() to inspect the exact prompt/messages
your agent produced — handy for verifying system prompts, context injection, or tool wiring.
use rig::test_utils::MockCompletionModel;
#[tokio::test]async fn preamble_is_sent_to_the_model() { let model = MockCompletionModel::text("ok"); let probe = model.clone();
let agent = AgentBuilder::new(model).preamble("You are a pirate.").build(); let _ = agent.prompt("Ahoy").await.unwrap();
let sent = probe.requests(); assert_eq!(sent.len(), 1); assert_eq!(sent[0].preamble.as_deref(), Some("You are a pirate."));}