Skip to content
Get Started

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

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 test
test agent_returns_the_scripted_reply ... ok

Real agents loop: the model asks to call a tool, Rig runs it, then the model answers. Script each turn with MockTurnMockTurn::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
}

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

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