Skip to main content
Unit tests exercise small, deterministic pieces of your agent in isolation. By replacing the real LLM with an in-memory fake (AKA fixture), you can script exact responses (text, tool calls, and errors) so tests are fast, free, and repeatable without API keys.

Mock chat model with fakeModel

fakeModel is a builder-style fake chat model that lets you script exact responses (text, tool calls, errors) and assert what the model received. It extends BaseChatModel, so it works anywhere a real model is expected.

Quick start

Create a model, queue responses with .respond(), and invoke. Each invoke() consumes the next queued response in order:
If the model is invoked more times than there are queued responses, it throws a descriptive error:

Tool calling responses

.respond() supports tool calls by passing an AIMessage with tool_calls:
.respondWithTools() is a shorthand for the same thing. Instead of constructing the full AIMessage, provide just the tool name and arguments:
The id field is optional. If omitted, a unique ID is auto-generated.
.respond() and .respondWithTools() can be mixed freely in any order. This is particularly useful for testing agentic loops where the model alternates between tool calls and text responses.

Simulate errors

Errors at specific turns

Passing an Error to .respond() makes the model throw on that specific invocation. Errors can appear at any position in the sequence:

Errors on every call

.alwaysThrow() makes every invocation throw, regardless of the queue. This is useful for testing error handling and retry logic:

Dynamic responses with factory functions

.respond() also accepts a function that computes the response based on the input messages. The function receives the full message array and returns either a BaseMessage or an Error:
Factory functions can also return errors:
Each function is a single queue entry, consumed once. To reuse the same dynamic logic for multiple turns, queue multiple respond function calls.

Structured output

For code that uses .withStructuredOutput(), configure the fake return value with .structuredResponse():
The schema passed to .withStructuredOutput() is ignored. The model always returns the value configured with .structuredResponse(). This keeps tests focused on application logic rather than parsing.

Assert what the model received

fakeModel records every invocation, including the messages and options passed to the model. This works like a spy or mock in traditional testing frameworks:
Calls are recorded even when the model throws:

Use with bindTools

Agent frameworks like LangChain agents and LangGraph call model.bindTools(tools) internally. fakeModel handles this automatically. The bound model shares the same response queue and call recording as the original, so no special setup is needed:

Next steps

Learn how to test your agent with real model provider APIs in Integration testing.