2. 测试金字塔与API测试

2.1 测试金字塔模型

测试金字塔(Test Pyramid)由 Mike Cohn 在 2009 年提出,是指导自动化测试分层投入的核心模型。它将测试按粒度、执行速度和维护成本分为三层:底层单元测试、中层集成测试、顶层端到端(E2E)测试。

核心原则: 底层测试数量多、执行快、维护成本低;顶层测试数量少、执行慢、维护成本高。团队应遵循“底层多写、顶层少写”的投入比例。
推荐比例参考: 单元测试约占 70%,集成测试约占 20%,E2E 测试约占 10%。该比例可根据项目复杂度适当调整,但不应颠倒。
测试金字塔模型 E2E 测试 数量少 · 速度慢 集成测试 数量中等 · 速度中等 单元测试 数量多 · 速度快 维护成本高 维护成本低

2.2 各层测试对比

维度 单元测试 集成测试 E2E 测试
测试范围 单个函数/方法/类 模块间交互/接口 完整用户流程
执行速度 毫秒级 秒级 分钟级
依赖隔离 完全隔离(Mock) 部分真实依赖 全部真实依赖
故障定位 精准到代码行 定位到模块边界 定位到流程步骤
维护成本 低 中 高
运行频率 每次提交 每日构建 预发布/回归

2.3 API测试在金字塔中的位置

API 测试横跨集成测试和部分单元测试两层,是金字塔中承上启下的关键环节。它既验证服务端接口的请求/响应契约,又通过 Mock 技术隔离外部依赖,实现快速、稳定的测试反馈。

  • 在集成测试层: 验证 API 端点与数据库、缓存、消息队列等真实组件的交互是否正确。
  • 在单元测试层: 对 API 处理函数(Handler/Controller)进行 Mock 测试,隔离 HTTP 请求和下游服务。
  • 不推荐在 E2E 层: 全链路 E2E 测试应覆盖关键业务流程,而非每个 API 端点。
常见误区: 团队常将大量 API 测试放在 E2E 层执行,导致测试执行时间过长、环境不稳定、故障定位困难。正确的做法是将 API 测试下沉到集成测试层,配合 Mock 实现快速反馈。
API 测试在金字塔中的位置 E2E 集成测试 API 测试主战场 单元测试 API Handler Mock 测试 API 测试策略 Mock 隔离外部依赖 验证请求/响应契约

2.4 Mock在API测试中的角色

Mock 是 API 测试中实现依赖隔离和故障模拟的核心技术。在测试金字塔中,Mock 主要服务于集成测试和单元测试两层,帮助测试人员摆脱外部依赖的束缚。

Mock 的核心作用

作用 说明 典型场景
依赖隔离 用模拟对象替代真实的外部服务、数据库或消息队列 第三方支付 API、短信网关
状态控制 精确控制被模拟服务的返回状态码、响应体和延迟 模拟 500 错误、超时、限流
行为验证 验证被测代码是否正确调用了 Mock 对象的方法和参数 确认请求头、请求体、调用次数
加速执行 消除网络 I/O 和外部处理延迟,将测试执行时间从秒级降至毫秒级 CI/CD 流水线中的快速反馈
故障注入 模拟真实环境中难以触发的异常和边界条件 数据库连接断开、磁盘满、认证失败

Mock 在 API 测试中的典型工作流

  1. 识别外部依赖: 列出 API 处理函数中调用的所有外部服务、数据库、缓存等。
  2. 创建 Mock 对象: 使用 Mock 框架(如 unittest.mock、pytest-mock)创建模拟对象。
  3. 配置预期行为: 设置 Mock 对象的返回值、异常或副作用。
  4. 执行被测 API: 调用 API 处理函数,触发对 Mock 对象的调用。
  5. 验证交互结果: 断言 Mock 对象是否被正确调用,以及 API 返回值是否符合预期。
Mock 使用原则: 只 Mock 自己不拥有的依赖(第三方服务、基础设施),不要 Mock 自己团队维护的内部模块。过度 Mock 会导致测试与实现耦合过紧,降低测试的有效性。

Mock 与 Stub 的区别

维度 Mock Stub
核心目的 验证行为(是否被调用、调用参数) 提供预设返回值
状态验证 支持(验证交互行为) 不支持(仅返回数据)
适用场景 需要验证调用顺序、次数、参数 只需要模拟返回值,不关心调用细节
复杂度 较高 较低
实践建议: 在 API 测试中优先使用 Stub 提供返回值,仅在需要验证调用行为(如日志记录、重试机制、回调通知)时才使用 Mock。保持测试简单,避免过度规格化。
# Python 示例:使用 pytest-mock 对 API 处理函数进行 Mock 测试
import pytest
from unittest.mock import Mock

def test_get_user_api(mocker):
    # 1. 创建 Mock 对象替代外部数据库查询
    mock_db = Mock()
    mock_db.get_user.return_value = {"id": 1, "name": "Alice"}

    # 2. 注入 Mock 到 API 处理函数
    mocker.patch("app.api.get_user_db", return_value=mock_db)

    # 3. 执行被测 API
    from app.api import get_user_handler
    result = get_user_handler(user_id=1)

    # 4. 验证结果
    assert result["name"] == "Alice"
    mock_db.get_user.assert_called_once_with(1)