Back to guides

GUIDE / agentic

LangChain Testing: Evaluating Chains and Agents

Learn LangChain testing for chains and agents with unit tests, mock LLMs, LangSmith-style evals, LangGraph checks, and RAG pipeline evaluation.

By The Testing AcademyPublished July 9, 2026Updated July 9, 202614 min read

If you are setting up LangChain testing, you need a strategy that matches how LangChain apps are built: composable runnables, chains, retrievers, tools, agents, and increasingly graph-based orchestration. A single live demo script is not a test suite. You want fast unit tests with mock LLMs, solid integration tests around tools and vector stores, and evaluation runs that catch quality regressions in prompts and RAG.

This guide covers unit testing chains and agents, mocking LLM responses and callbacks, evaluating RAG pipelines, using LangSmith-style evaluation workflows, testing LangGraph agents, and avoiding the common traps that make LangChain projects feel untestable.

What You Are Testing in a LangChain App

LangChain applications usually combine:

  • Prompt templates.
  • Models (chat/completions).
  • Output parsers and structured outputs.
  • Retrievers and document loaders.
  • Tools and tool-calling agents.
  • Memory or state.
  • Graphs/workflows (LangGraph).
  • Callbacks and tracing.

Each piece can fail independently. Good LangChain testing separates pure logic from model non-determinism.

Test Pyramid for LangChain

LevelScopeSpeedDeterminismExamples
UnitOne function/runnable with mocksFastHighParser, prompt build, tool arg validate
ComponentChain with fake model + fake retrieverFastHighRAG chain returns citation object
IntegrationReal local vector DB or sandbox APIMediumMediumIndex + retrieve + answer with fixtures
Eval / regressionDataset scoring, maybe live modelSlowMediumGolden Q&A quality gates
E2E productFull app UI or APISlowLowerUser asks, ticket created

If everything is E2E against a live model, you will hate your CI.

Unit Testing LangChain Chains

Principles

  1. No network in unit tests unless the unit is an HTTP client wrapper with mocked transport.
  2. Inject dependencies: model, retriever, tools should be constructor parameters.
  3. Assert contracts: message shape, tool calls, parsed fields, state updates.
  4. Keep prompts snapshot-tested when structure matters, not when every wording tweak should fail CI.
  5. Test error paths: empty docs, malformed model JSON, tool exceptions.

Example: Testing a Prompt Plus Parser Chain

Conceptual pattern (Python-like pseudocode):

def test_extract_invoice_fields_with_fake_model():
    fake = FakeChatModel(responses=[
        AIMessage(content='{"vendor":"Acme","total":12.5,"currency":"USD"}')
    ])
    chain = build_invoice_chain(model=fake)
    result = chain.invoke({"text": "Invoice from Acme for $12.50"})
    assert result["vendor"] == "Acme"
    assert result["total"] == 12.5
    assert fake.last_messages[0].content.contains("Invoice from Acme")

What this proves:

  • Your chain wiring works.
  • Parser accepts the model output shape.
  • Prompt received the user input.

What it does not prove:

  • A real model will always emit valid JSON.
  • Extraction quality on messy invoices.

That is what eval datasets are for.

Mock LLM Responses in LangChain

Common mock strategies:

  • Fake list model: returns a queue of predetermined messages.
  • Callable fake: chooses response based on input content.
  • Tool-call fakes: return tool_calls in the assistant message for agent tests.
  • Error fakes: raise rate limit or timeout once, then succeed, to test retries.

Also mock callbacks when you assert token usage accounting or custom logging:

def test_callback_records_tokens():
    cb = RecordingCallback()
    fake = FakeChatModel(responses=[AIMessage(content="ok")], callbacks=[cb])
    chain = prompt | fake
    chain.invoke({"q": "hi"})
    assert cb.prompt_tokens >= 0  # or exact if fake sets usage
    assert cb.starts == 1
    assert cb.ends == 1

Mock LLM callbacks for LangChain tests keep observability code covered without live runs.

Testing Tools and Agents

Agents multiply complexity. Apply the same ideas as in testing AI agents, with LangChain-specific seams.

Tool Unit Tests

Test tools as plain functions:

  • Valid args produce correct side effects.
  • Invalid args raise or return structured errors.
  • Auth boundaries enforced.
  • No real production credentials in tests.

Agent Tests With Fake Models

Drive the agent through scripted model outputs:

  1. First model turn: tool call search.
  2. Tool returns fixture.
  3. Second model turn: final answer using fixture.

Assert:

  • Tool was called with expected args.
  • Final answer includes fixture facts.
  • No extra write tools called.

This is trajectory testing without paying for tokens.

Structured Tool Calling

If you use function/tool calling, validate schemas and argument parsing the same way you would in function-calling LLM tests.

Evaluating LangChain RAG Pipelines

RAG quality needs dataset evaluation, not only unit tests.

Split the Pipeline

StageMetrics
IndexingDocs count, chunk counts, failed loads
RetrievalRecall@k, MRR, context precision proxies
GenerationFaithfulness, answer relevancy, correctness
End to endTask success, citations, latency, cost

If you only measure final answer scores, you cannot tell whether to fix the retriever or the prompt.

Golden Set Shape for LangChain RAG

{
  "id": "rag-042",
  "question": "What is the return window for keyboards?",
  "expected_doc_ids": ["policy_returns_v3"],
  "expected_answer_properties": [
    "mentions_30_days",
    "does_not_invent_restocking_fee"
  ],
  "tags": ["returns", "hardware"]
}

Runner outline:

  1. Invoke chain with question.
  2. Capture retrieved docs from run trace or chain debug output.
  3. Score retrieval hit against expected_doc_ids.
  4. Score answer with rubric or automatic faithfulness metric.
  5. Store results by chain version.

For metric deep dives, see RAGAS RAG evaluation and LLM evaluation metrics.

Deterministic Context Tests

To isolate generation prompts, bypass retrieval and pass fixed documents into the chain. This answers: "If retrieval is perfect, does the prompt still hallucinate?"

LangChain LangSmith Evaluation Tutorial (Practical Workflow)

Whether you use LangSmith or an alternative tracing/eval platform, the workflow looks like this:

  1. Trace local runs while developing a chain.
  2. Create a dataset from hand-written cases and sanitized prod samples.
  3. Define evaluators: exact match, LLM-as-judge rubrics, custom Python checks.
  4. Run experiments comparing chain versions (prompt A vs B, model X vs Y).
  5. Inspect diffs on failed examples using traces (retrieved docs, tool calls).
  6. Pin a baseline and gate releases on regressions.

What Belongs in CI vs LangSmith Experiments

In PR CIIn scheduled/release eval
Unit tests with fakesFull golden set on live or pinned model
Schema/parser testsJudge-based rubrics
Fixture agent trajectoriesCost and latency trends
Prompt snapshot for critical safety textMulti-model comparisons

Use the platform for visibility and collaboration. Keep hard contracts in ordinary test runners so failures are obvious.

Testing LangGraph Agents

LangGraph models agents as stateful graphs. That is a gift for testers.

Node Unit Tests

Each node is a function: state in, state out.

def test_plan_node_adds_steps():
    state = {"goal": "Book a slot", "messages": []}
    new_state = plan_node(state, model=FakePlanner())
    assert "steps" in new_state
    assert len(new_state["steps"]) >= 1

Edge and Routing Tests

  • Given state with needs_human=True, next node is interrupt/wait.
  • Given tool error count > N, route to fallback.
  • Given success criteria met, route to end.

Graph-Level Scenario Tests

Run the compiled graph with mocked node dependencies:

  • Assert final state fields.
  • Assert max iterations not exceeded.
  • Assert checkpoint/resume behavior if you use persistence.
  • Assert human-in-the-loop pauses emit the right interrupt payload.

Loop Protection

Explicitly test that graphs cannot spin forever when the model keeps requesting tools. Cap steps and assert termination with a controlled error state.

Callbacks, Tracing, and Flake Control

Assertions on Intermediate Steps

Prefer public/state outputs over scraping internals. When you must assert intermediate behavior, use:

  • Returned intermediate from invoke config when available.
  • Your own instrumented wrappers.
  • Traces in integration environments.

Controlling Non-Determinism

  • Fake models in unit tests.
  • Temperature 0 and pinned model for live smoke.
  • Multiple samples only in eval jobs.
  • Retry only intentional recovery tests.

Token and Cost Guards

Add tests that fail if a chain exceeds a step or tool budget on fixture scenarios. Cost regressions are product regressions.

Worked Example: Support Chain Test Plan

Components:

  • Classifier chain.
  • Retriever over help center.
  • Tool: order_lookup.
  • Final response synthesizer.

Unit

  • Classifier maps sample texts to labels with fake model outputs.
  • Synthesizer refuses when no context and no tool result.

Component

  • Fixed docs + fake model => grounded answer object with citations.

Agent/Graph

  • Scripted tool call path for order questions.
  • Forbidden refund tool never called on fixture policy.

Eval

  • 80 case golden set: retrieval + answer properties.
  • Safety set: injection in ticket text.

CI

  • Unit/component on every PR.
  • Eval smoke 20 cases on prompt changes.
  • Full eval nightly.

Common Mistakes in LangChain Testing

Mistake 1: Only Manual Playground Clicks

Playgrounds do not protect main branch.

Mistake 2: Live Model Calls in Every Unit Test

Slow, flaky, expensive, and often blocked in CI.

Mistake 3: Untestable Global Singletons

If the model is hard-coded inside the chain module, mocking becomes painful. Inject dependencies.

Mistake 4: Asserting Full Prompt Strings for Every Change

Brittle snapshots create alert fatigue. Snapshot structure and safety-critical clauses selectively.

Mistake 5: RAG Evals Without Retrieval Metrics

You will tune the wrong stage.

Mistake 6: Ignoring Tool Failure Paths

Agents look great until the API times out.

Mistake 7: No Dataset Versioning

Scores become incomparable across weeks.

Mistake 8: Treating Traces as Tests

Traces help debug. Assertions protect users.

Practical Checklist

  • Chains accept injected models/retrievers/tools.
  • Unit tests cover parsers, routing, and tool wrappers with fakes.
  • Agent trajectories scripted for critical flows.
  • RAG golden set exists with retrieval and generation scores.
  • LangSmith or equivalent experiments used for version compare.
  • LangGraph nodes and edges unit tested.
  • Loop limits and error routes verified.
  • CI split: fast mocks vs scheduled evals.
  • Cost/latency budgets tracked on smoke scenarios.
  • Failures from prod traces become dataset cases.

Dependency Injection Patterns That Make Testing Easy

Hard to test:

def answer(question: str) -> str:
    llm = ChatOpenAI(model="gpt-4.1")
    retriever = build_prod_retriever()
    return (prompt | llm).invoke({"q": question, "docs": retriever.invoke(question)})

Easier to test:

def build_answer_chain(llm, retriever):
    def _run(question: str):
        docs = retriever.invoke(question)
        return (prompt | llm).invoke({"q": question, "docs": docs})
    return _run

In tests, pass FakeListChatModel and a lambda retriever. In production, pass real implementations. This single design choice determines whether LangChain testing is pleasant or painful.

Snapshot Strategy for Prompts

Full prompt snapshots fail on every comma change. Prefer:

  • Snapshot the template structure and required variables.
  • Assert safety strings that must remain present.
  • Assert that user content is interpolated in the right message role.
  • Leave stylistic wording to eval suites, not unit snapshots.

Example assertions:

msgs = build_messages(question="hi", docs=["policy"])
assert msgs[0].type == "system"
assert "do not invent policy" in msgs[0].content.lower()
assert msgs[-1].content == "hi"

Evaluating Streaming Chains

Streaming UIs introduce bugs:

  • Partial JSON shown to users.
  • Tool calls streamed incorrectly.
  • Final state differs from concatenated chunks.
  • Cancellation leaves orphan tool calls.

Tests:

  • Collect stream chunks from a fake model that emits token lists.
  • Assert concatenated content equals non-streaming invoke for the same fake output.
  • Assert parsers wait for complete structured output when required.
  • Assert cancel path closes resources.

Vector Store Integration Tips

Local integration tests can use ephemeral stores:

  • Seed three docs with known IDs.
  • Query a string only doc 2 should match.
  • Assert retriever returns doc 2 in top k.
  • Delete collection in teardown.

Keep these tests out of pure unit suites if they need disk or Docker, but keep them in CI service jobs. Retrieval bugs are common and expensive to discover only through answer quality scores.

LangGraph Persistence and Resume Tests

If state is checkpointed:

  1. Run graph until interrupt.
  2. Persist checkpoint id.
  3. Resume with human approval payload.
  4. Assert the write tool executes once.
  5. Crash before resume and restore again to ensure no duplicate side effects.

These tests protect real money and real tickets.

Flaky Test Control in LLM Stacks

Classify flakes:

  • Model variance: move to eval rates, not binary unit asserts.
  • Network: mock or pin sandbox.
  • Clock: freeze time for expiring tokens.
  • Unordered sets: compare as sets, not lists.
  • Async races: await completion events, do not sleep hopefully.

A flaky agent suite trains the team to ignore red builds. That is fatal for safety gates.

Migration Testing When Upgrading LangChain

Upgrades can change default parsing, message formats, or tool call shapes.

Checklist:

  • Run unit suite on the new version in a branch.
  • Re-record fakes if message types changed.
  • Re-run golden evals and compare to baseline.
  • Read changelog for tool calling and callbacks.
  • Canary in staging with trace sampling before full release.

Treat framework upgrades like model upgrades: measured, not hopeful.

Example CI Job Map

JobTriggersContents
unitevery PRfakes, parsers, graph nodes
integrationevery PRlocal retriever + sandbox tools
eval-smokePR labels prompt/model20 golden cases
eval-fullnightlyfull golden + safety
upgrade-canarymanual/workflowversion bump experiments

Keep feedback under ten minutes for PR jobs whenever possible.

Output Parser Battle Tests

Parsers fail in boring ways that break production:

  • Model wraps JSON in markdown fences.
  • Extra trailing commas.
  • Numeric fields as strings.
  • Enum casing drift.
  • Partial answers when max tokens is low.

Build a table-driven parser suite with ugly but realistic strings. Prefer robust parsing with clear errors over silent coercion of dangerous fields like amounts and IDs.

Memory and Chat History Tests

If the chain uses history:

  • Verify history is truncated by policy (token or turn limits).
  • Verify system messages are not duplicated unboundedly.
  • Verify user A history cannot appear in user B runs.
  • Verify tool messages remain correctly ordered after retries.

History bugs create both quality and privacy failures.

For deeper coverage of persistence, truncation, and privacy leakage, extend this section with testing LLM memory and context.

Retrieval-Augmented Generation Isolation Pattern

Three test modes for every major RAG change:

  1. Retriever only: fixed questions, assert doc IDs.
  2. Generator only: fixed docs, assert grounded answers.
  3. Combined: full chain, assert both.

This isolation pattern saves days of finger pointing between search quality and prompt quality.

Definition of Done for a LangChain PR

  • Unit tests updated for new branches and parsers.
  • Fakes cover new tool call shapes.
  • Eval smoke run if prompts or models changed.
  • Traces inspected for one happy and one failure path.
  • Cost note if the change adds steps or larger context.
  • Docs updated for new env vars and tool permissions.

Socialize this definition so "works in my notebook" stops being a merge argument.

Smoke Tests Versus Full Evals in Practice

A useful rule: if a failure must block a pull request within minutes, it belongs in unit or component tests with mocks. If a failure needs a judge model, multi-sample scoring, or a 200-case golden set, it belongs in scheduled evaluation.

Teams get into trouble when they reverse this. They put slow live evals on every commit and then bypass CI when it becomes annoying. They also get into trouble when they only run evals manually before big launches and miss weekly prompt drift.

Write the policy down:

  • Prompt text change: unit + eval smoke.
  • Tool schema change: unit + agent trajectory fixtures.
  • Model pin change: full eval required.
  • Refactor with no prompt or model change: unit only if behavior is identical.

Policy clarity keeps LangChain testing sustainable for the humans who maintain it.

Final Workflow

  1. Refactor for dependency injection if needed.
  2. Add fake-model unit tests for each chain.
  3. Add tool and parser contract tests.
  4. Build a small golden eval set.
  5. Wire tracing and experiment comparison.
  6. Add graph/node tests for agentic flows.
  7. Gate PRs on unit/component; gate releases on eval.
  8. Grow the dataset from production mistakes.

LangChain testing is ordinary software testing plus evaluation discipline. Mock what must be deterministic, evaluate what is probabilistic, and never confuse a green demo with a release quality bar. When you want broader agent QA patterns beyond the framework, read testing AI agents. To train sharp scenario design skills, practice in the QABattle arena.

FAQ

Questions testers ask

How do you unit test LangChain chains and agents?

Unit test LangChain components by isolating pure functions, mocking LLM and tool responses, asserting prompts and parsed outputs, and keeping network calls out of unit scope. Use integration tests with fixtures for retrievers and tools, and evaluation suites for quality regressions.

How do you mock LLM responses in LangChain?

Inject a fake chat model or runnable that returns predetermined messages, tool calls, or errors. Assert that your chain invoked it with expected messages, then validate parsing, branching, and tool handling without paying for live tokens or accepting non-determinism in unit tests.

How do you evaluate LangChain RAG pipelines?

Build a golden set of questions with contexts or doc IDs, run the chain, and score answer quality, groundedness, citation correctness, retrieval hit rate, latency, and cost. Separate retriever metrics from generation metrics so you can see which stage failed.

What is LangSmith used for in testing?

LangSmith helps trace runs, dataset-driven evaluations, compare chain versions, and inspect intermediate steps. Use it to debug production-like traces and to run evaluation experiments, while still keeping critical contract tests in ordinary CI unit suites.

How do you test LangGraph agents?

Test LangGraph by unit testing node functions, mocking model and tool nodes, asserting state transitions and edges, and running graph-level scenarios that check final state, loop limits, and interrupt or human-in-the-loop paths.

Should LangChain tests call real models in CI?

Keep most PR checks on mocks and deterministic fixtures. Run a small live-model smoke on schedule or release branches if needed, with budgets and pinned models, so CI stays fast and costs stay controlled.