> ## Documentation Index
> Fetch the complete documentation index at: https://cndoc-langchain.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 集成测试

> 通过组织测试、管理密钥、处理不稳定性和控制成本，使用真实 LLM API 测试智能体。

集成测试用于验证您的智能体能否与模型 API 和外部服务正确协作。与使用伪造和模拟的[单元测试](/oss/python/langchain/test/unit-testing)不同，集成测试会进行实际的网络调用，以确认组件协同工作、凭证有效且延迟可接受。

由于 LLM 响应具有不确定性，集成测试需要采用与传统软件测试不同的策略。本指南介绍如何为您的智能体组织、编写和运行集成测试。有关为 LangChain 本身贡献代码时的通用测试基础设施，请参阅[贡献代码](/oss/python/contributing/code#running-tests)。

## 分离单元测试和集成测试

集成测试较慢且需要 API 凭证，因此应将其与单元测试分开。这样您可以在每次更改时快速运行单元测试，而将集成测试保留用于 CI 或部署前检查。

使用 pytest 标记来标记集成测试：

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
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 以识别该标记，并在默认运行中排除集成测试：

<CodeGroup>
  ```ini pytest.ini theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  [pytest]
  markers =
      integration: tests that call real LLM APIs
  addopts = -m "not integration"
  ```

  ```toml pyproject.toml theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  [tool.pytest.ini_options]
  markers = [
    "integration: tests that call real LLM APIs"
  ]
  addopts = "-m 'not integration'"
  ```
</CodeGroup>

显式运行集成测试：

```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
pytest -m integration
```

## 管理 API 密钥

集成测试需要真实的 API 凭证。从环境变量加载它们，以确保密钥不会进入源代码控制。

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

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
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`](https://pypi.org/project/python-dotenv/) 加载它们：

```bash .env theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
OPENAI_API_KEY=sk-...
```

```python conftest.py theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
from dotenv import load_dotenv

load_dotenv()
```

<Warning>
  将 `.env` 添加到您的 `.gitignore` 中，以避免提交凭证。在 CI 中，通过您的提供商的密钥管理（例如 GitHub Actions secrets）注入密钥。
</Warning>

## 断言结构，而非内容

LLM 响应在每次运行时都不同。不要断言精确的输出字符串，而是验证响应的结构属性：消息类型、工具调用名称、参数形状和消息数量。

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
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
```

<Tip>
  对于更严格的轨迹断言，请使用 [AgentEvals](/oss/python/langchain/test/evals) 评估器，它们支持模糊匹配模式，如 `unordered` 和 `superset`。
</Tip>

## 降低成本和延迟

调用 LLM API 的集成测试会产生实际成本。以下一些实践有助于保持测试套件快速且经济：

* **使用较小的模型**：对于仅需验证工具调用和响应结构的测试，使用 `gemini-3.1-flash-lite-preview` 或同等模型。
* **设置 `maxTokens`**：限制响应长度，以避免冗长且昂贵的补全。
* **限制测试范围**：每个测试只测试一种行为。当单轮测试足够时，避免链接多个 LLM 调用的端到端场景。
* **选择性运行**：使用[上述](#分离单元测试和集成测试)的测试分离方法，仅在 CI 或部署前运行集成测试，而不是在每次保存文件时运行。

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
agent = create_agent(
    "gemini-3.1-flash-lite-preview",
    tools=[get_weather],
    model_kwargs={"max_tokens": 256},
)
```

## 记录和重放 HTTP 调用

对于在 CI 中频繁运行的测试，您可以在第一次运行时记录 HTTP 交互，并在后续运行中重放它们，而无需进行真实的 API 调用。这消除了初始记录后的成本和延迟。

[`vcrpy`](https://pypi.org/project/vcrpy/1.5.2/) 将 HTTP 请求/响应对记录到 YAML "盒式文件"中。[`pytest-recording`](https://pypi.org/project/pytest-recording/) 插件将其与 pytest 集成。

设置您的 `conftest.py` 以从盒式文件中过滤敏感信息：

```py conftest.py theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
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` 标记：

<CodeGroup>
  ```ini pytest.ini theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  [pytest]
  markers =
      vcr: record/replay HTTP via VCR
  addopts = --record-mode=once
  ```

  ```toml pyproject.toml theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  [tool.pytest.ini_options]
  markers = [
    "vcr: record/replay HTTP via VCR"
  ]
  addopts = "--record-mode=once"
  ```
</CodeGroup>

<Info>
  `--record-mode=once` 选项在第一次运行时记录 HTTP 交互，并在后续运行中重放它们。
</Info>

使用 `vcr` 标记装饰您的测试：

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
@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/` 中生成一个盒式文件。后续运行会重放记录的响应。

<Warning>
  当您修改提示、添加新工具或更改预期轨迹时，您保存的盒式文件将过时，您现有的测试**将会失败**。删除相应的盒式文件并重新运行测试以记录新的交互。
</Warning>

## 后续步骤

了解如何在 [Evals](/oss/python/langchain/test/evals) 中使用确定性匹配或 LLM 作为评判的评估器来评估智能体轨迹。

***

<div className="source-links">
  <Callout icon="terminal-2">
    [将这些文档](/use-these-docs)通过 MCP 连接到 Claude、VSCode 等，以获取实时答案。
  </Callout>

  <Callout icon="edit">
    [在 GitHub 上编辑此页面](https://github.com/langchain-ai/docs/edit/main/src/oss/langchain/test/integration-testing.mdx) 或 [提交问题](https://github.com/langchain-ai/docs/issues/new/choose)。
  </Callout>
</div>
