以编程方式使用文档

集成测试验证您的智能体是否与模型 API 和外部服务正常协作。与 单元测试 使用 fake 和 mock 不同,集成测试会发起真实的网络调用,以确认组件能协同工作、凭据有效以及延迟可接受。

由于 LLM 响应具有非确定性,集成测试需要与传统软件测试不同的策略。本指南涵盖如何为您的智能体组织、编写和运行集成测试。关于为 LangChain 本身贡献时的通用测试基础设施,请参阅 代码贡献.

分离单元测试和集成测试

集成测试较慢且需要 API 凭据,因此请将它们与单元测试分开。这样您可以在每次更改时运行快速单元测试,仅在 CI 或预部署检查时运行集成测试。

使用 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"
[tool.pytest.ini_options]
markers = [
  "integration: tests that call real LLM APIs"
]
addopts = "-m 'not integration'"

显式运行集成测试:

pytest -m integration

管理 API 密钥

集成测试需要真实的 API 凭据。从环境变量加载它们,以便密钥不会进入源代码管理。

使用 conftest.py fixture 来验证所需的密钥是否可用:

@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:

OPENAI_API_KEY=sk-...
from dotenv import load_dotenv

load_dotenv()

断言结构而非内容

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

降低成本和延迟

调用 LLM API 的集成测试会产生真实成本。以下做法有助于保持测试套件快速且经济实惠:

  • 使用更小的模型: gemini-3.1-flash-lite 或等效模型用于只需要验证工具调用和响应结构的测试。
  • - **设置 maxTokens**:限制响应长度以避免冗长且昂贵的补全。
  • 限制测试范围:每个测试验证一个行为。当单轮测试足够时,避免链接多个 LLM 调用的端到端场景。
  • 选择性运行:使用上面的测试分离功能 仅在 CI 或部署前运行集成测试,而非在每次保存文件时。
agent = create_agent(
    "gemini-3.1-flash-lite",
    tools=[get_weather],
    model_kwargs={"max_tokens": 256},
)

录制和回放 HTTP 调用

对于在 CI 中频繁运行的测试,你可以在首次运行时录制 HTTP 交互,并在后续运行中回放它们,而无需发起真实的 API 调用。这消除了首次录制后的成本和延迟。

vcrpy records HTTP request/response pairs into YAML "cassette" files. The pytest-recording 插件将其与 pytest 集成。

配置你的 conftest.py 来过滤 cassette 中的敏感信息:

@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 marker:

[pytest]
markers =
    vcr: record/replay HTTP via VCR
addopts = --record-mode=once
[tool.pytest.ini_options]
markers = [
  "vcr: record/replay HTTP via VCR"
]
addopts = "--record-mode=once"

使用装饰器装饰你的测试 vcr marker:

@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 [])
    )

首次运行会发起真实的网络调用并生成一个 cassette 文件,位于 tests/cassettes/。后续运行将回放已录制的响应。

后续步骤

了解如何在 中通过确定性匹配或 LLM 评判器来评估代理轨迹.