| name | langchain-agent-unit-testing |
| description | Unit-test LangChain agents without API calls using GenericFakeChatModel and in-memory checkpointers. Use when writing pytest tests for create_agent flows, multi-turn state, or scripted tool-call sequences; when avoiding live LLM keys; or when the user mentions fake chat models, agent fixtures, or InMemorySaver for tests. |
| disable-model-invocation | true |
Documentation Index
Fetch the complete documentation index at: https://docs.langchain.com/llms.txt
Use this file to discover all available pages before exploring further.
Unit testing LangChain agents
Replace the real LLM with a scripted fake so tests are fast, deterministic, and need no API keys.
Mock chat model — GenericFakeChatModel
from langchain_core.language_models.fake_chat_models import GenericFakeChatModel
from langchain_core.messages import AIMessage
model = GenericFakeChatModel(
messages=iter([
AIMessage(
content="",
tool_calls=[
{
"name": "foo",
"args": {"bar": "baz"},
"id": "call_1",
"type": "tool_call",
}
],
),
"bar",
])
)
model.invoke("hello")
model.invoke("again")
Each invoke / ainvoke consumes the next item from the iterator. Use AIMessage objects for tool calls, structured output, or empty content; use plain str for simple text replies.
Checkpointer — InMemorySaver
Persist thread state across invocations in tests (same as production pattern, in RAM only).
from langchain.agents import create_agent
from langchain_core.language_models.fake_chat_models import GenericFakeChatModel
from langchain_core.messages import AIMessage, HumanMessage
from langgraph.checkpoint.memory import InMemorySaver
model = GenericFakeChatModel(
messages=iter([
AIMessage(content="Acknowledged."),
AIMessage(content="You said you live in Sydney (GMT+10)."),
])
)
agent = create_agent(model, tools=[], checkpointer=InMemorySaver())
cfg = {"configurable": {"thread_id": "session-1"}}
agent.invoke(
{"messages": [HumanMessage(content="I live in Sydney, Australia")]},
config=cfg,
)
out = agent.invoke(
{"messages": [HumanMessage(content="What did I tell you about my city?")]},
config=cfg,
)
MemorySaver in some docs is the same implementation family as InMemorySaver; either import from langgraph.checkpoint.memory per your installed LangGraph.
create_agent + tools — important limitation
create_agent binds tools to the model. GenericFakeChatModel inherits the default bind_tools from the base class, which raises NotImplementedError.
Practical split:
| Goal | Approach |
|---|
| Multi-turn memory, routing, middleware with no tools | create_agent(fake_model, tools=[], checkpointer=InMemorySaver()) — works. |
Tool-calling agent loop under create_agent | Integration test with a real small model, or mock/patch the bound model, or test the tool executor / graph node in isolation with a custom fake that implements bind_tools. |
LangGraph StateGraph nodes (plain functions) | Call nodes directly with dict state; fake model only where you invoke a model yourself. |
Key rules
- Iterator length — ensure enough scripted messages for every model step in the test (including follow-up turns after tools).
thread_id — always pass config={"configurable": {"thread_id": "..."}} when using a checkpointer.
- Fresh checkpointer — construct a new
InMemorySaver() per test to avoid cross-test leakage.
- Tool call shape — use dict
tool_calls entries with name, args, id, type: "tool_call" unless your LangChain version documents a ToolCall helper.
Related
- LangGraph graph tests (nodes,
MemorySaver): langgraph-testing in this repo.
- Live provider tests: LangChain docs — Integration testing (
/oss/python/langchain/test/integration-testing).