集成测试验证您的代理与模型 API 和外部服务一起正常工作。与使用假和模拟的单元测试不同,集成测试进行实际网络调用以确认组件协同工作、凭证有效且延迟可接受。 由于 LLM 响应是不确定的,集成测试需要与传统软件测试不同的策略。本指南介绍如何组织、编写和运行代理的集成测试。有关为 LangChain 本身做贡献时的一般测试基础设施,请参阅贡献代码

分离单元测试和集成测试

集成测试较慢且需要 API 凭证,因此将它们与单元测试分开。这使您可以在每次更改时运行快速的单元测试,并将集成测试保留用于 CI 或部署前检查。 使用 pytest 标记来标记集成测试:
import pytest

@pytest.mark.integration
def test_agent_with_real_model():
    agent = create_agent("claude-sonnet-4-6", tools=[get_weather])
    result = agent.invoke({
        "messages": [HumanMessage(content="What's the weather in SF?")]
    })
    assert len(result["messages"]) > 1
配置 pytest 以识别标记并从默认运行中排除集成测试:
[pytest]
markers =
    integration: tests that call real LLM APIs
addopts = -m "not integration"
显式运行集成测试:
pytest -m integration

管理 API 密钥

集成测试需要真实的 API 凭证。从环境变量加载它们,使密钥保持在源代码控制之外。 使用 conftest.py 装置来验证所需的密钥是否可用:
import os
import pytest

@pytest.fixture(autouse=True)
def check_api_keys():
    if not os.environ.get("OPENAI_API_KEY"):
        pytest.skip("OPENAI_API_KEY not set")
对于本地开发,将密钥存储在 .env 文件中并使用 python-dotenv 加载它们:
.env
OPENAI_API_KEY=sk-...
conftest.py
from dotenv import load_dotenv

load_dotenv()
.env 添加到您的 .gitignore 以避免提交凭证。在 CI 中,通过提供商的密钥管理(例如,GitHub Actions 密钥)注入密钥。

断言结构,而不是内容

LLM 响应在运行之间有所不同。不要断言精确的输出字符串,而是验证响应的结构属性:消息类型、工具调用名称、参数形状和消息计数。
def test_agent_calls_weather_tool():
    agent = create_agent("claude-sonnet-4-6", tools=[get_weather])
    result = agent.invoke({
        "messages": [HumanMessage(content="What's the weather in SF?")]
    })

    messages = result["messages"]
    tool_calls = [
        tc
        for msg in messages
        if hasattr(msg, "tool_calls")
        for tc in (msg.tool_calls or [])
    ]

    assert any(tc["name"] == "get_weather" for tc in tool_calls)
    assert isinstance(messages[-1], AIMessage)
    assert len(messages[-1].content) > 0
有关更严格的轨迹断言,请使用 AgentEvals 评估器,它支持模糊匹配模式,如 unorderedsuperset

降低成本和延迟

调用 LLM API 的集成测试会产生实际成本。一些实践有助于保持测试套件快速且经济:
  • 使用较小的模型gemini-3.1-flash-lite-preview 或同等模型用于只需要验证工具调用和响应结构的测试。
  • 设置 maxTokens:限制响应长度以避免长而昂贵的完成。
  • 限制测试范围:每个测试测试一个行为。避免端到端场景,这些场景链接许多 LLM 调用,当单轮测试足够时。
  • 选择性地运行:使用上述的测试分离,仅在 CI 中或部署前运行集成测试,而不是在每次文件保存时。
agent = create_agent(
    "gemini-3.1-flash-lite-preview",
    tools=[get_weather],
    model_kwargs={"max_tokens": 256},
)

记录和重放 HTTP 调用

对于在 CI 中频繁运行的测试,您可以在第一次运行时记录 HTTP 交互,并在后续运行中重放它们,而无需进行真实的 API 调用。这在初始记录后消除了成本和延迟。 vcrpy 将 HTTP 请求/响应对记录到 YAML “cassette” 文件中。pytest-recording 插件将其与 pytest 集成。 设置您的 conftest.py 以过滤磁带中的敏感信息:
conftest.py
import pytest

@pytest.fixture(scope="session")
def vcr_config():
    return {
        "filter_headers": [
            ("authorization", "XXXX"),
            ("x-api-key", "XXXX"),
        ],
        "filter_query_parameters": [
            ("api_key", "XXXX"),
            ("key", "XXXX"),
        ],
    }
配置您的项目以识别 vcr 标记:
[pytest]
markers =
    vcr: 通过 VCR 记录/重放 HTTP
addopts = --record-mode=once
--record-mode=once 选项在第一次运行时记录 HTTP 交互,并在后续运行中重放它们。
vcr 标记装饰您的测试:
@pytest.mark.vcr()
def test_agent_trajectory():
    agent = create_agent("claude-sonnet-4-6", tools=[get_weather])
    result = agent.invoke({
        "messages": [HumanMessage(content="What's the weather in SF?")]
    })
    assert any(
        tc["name"] == "get_weather"
        for msg in result["messages"]
        if hasattr(msg, "tool_calls")
        for tc in (msg.tool_calls or [])
    )
第一次运行进行实际网络调用并在 tests/cassettes/ 中生成磁带文件。后续运行重放记录的响应。
当您修改提示、添加新工具或更改预期轨迹时,您保存的磁带将过时,您的现有测试将失败。删除相应的磁带文件并重新运行测试以记录新的交互。

下一步

了解如何在 评估 中使用确定性匹配或 LLM 评判评估器评估代理轨迹。