Lesson 25 / 29
Testing Graphs Without a Model
Unit-test nodes, routers and whole flows with scripted fakes.
Most of an agent is ordinary code
Nodes and routers are plain functions, so test them directly with sample states. For whole-graph tests, replace the model node with a scripted fake that returns a fixed sequence (as in the tool-loop example) and assert on the final state, the nodes visited and the stop reason. Cover the unhappy paths: a tool error, an invalid tool name, the loop cap, a rejected approval, a thread resumed after an interrupt. Keep a small suite against the real model for quality, run separately and less often, since real outputs vary. Snapshot the graph's structure (nodes and edges) so accidental changes to the flow show up in review.
Make agents dependable
Test nodes and routes without a model, evaluate whole trajectories, and deploy with tracing and guardrails.
A graph test with a scripted model (illustrative)
Uses the same style as the tool-loop example above: no network, deterministic. Not run as a test here.
def test_agent_stops_after_final_answer():
app = build_app(model_script=[
{"tool": "add", "args": {"a": 2, "b": 3}},
{"final": "5"},
])
out = app.invoke({"messages": [{"user": "2+3?"}]}, {"recursion_limit": 10})
assert out["messages"][-1] == {"final": "5"}
assert {"tool_result": 5} in out["messages"]
def test_unknown_tool_is_reported_not_executed():
app = build_app(model_script=[{"tool": "rm", "args": {}}, {"final": "sorry"}])
out = app.invoke({"messages": [{"user": "delete everything"}]}, {"recursion_limit": 10})
assert {"tool_result": "error: unknown tool"} in out["messages"]Quick check: How do you test a whole graph without calling a real LLM?
- Call production for every test
- You cannot
- Replace the model node with a scripted fake
- Delete the graph
Answer
Replace the model node with a scripted fake — A deterministic fake makes tests fast, free and repeatable.