很多团队把测试金字塔当成一张比例图:单元测试最多,集成测试少一些,端到端测试最少。Tom Akehurst 讨论的重点是这套比例背后的经济模型:写和维护测试要花多少力气、反馈有多快、失败后多久能找到原因,以及重构后测试能否继续验证可观察的行为。这些成本共同决定一套测试组合值不值得投入。

要重新评估的是图形背后的成本假设。本文先梳理 WireMock 原文,再补充测试边界与落地检查方法;实践部分是 QA 工程延伸,不是原作者给出的统一方案。

阅读来源:Tom Akehurst,《The Testing Pyramid Is an Outdated Economic Model》,WireMock,2026-08-05。原文将概念追溯到 Mike Cohn,并链接了测试金字塔、测试奖杯和 Mock API 的相关资料。

金字塔描述的是一种取舍

WireMock 原文把测试金字塔(也称测试自动化金字塔)归于 Mike Cohn:这一概念在 2004 年以“测试自动化金字塔”的形式提出,并在 2009 年的《Succeeding with Agile》中得到系统阐述。经典图形从下到上分为三层:数量较多、反馈较快的单元测试;数量较少、验证模块协作的集成测试;以及数量最少、模拟真实用户流程的端到端(E2E)测试。

层级通常验证什么常见优势常见成本
单元测试函数、类或局部业务规则执行快、成本低、容易自动化,适合覆盖大量输入组合如果绑定内部实现,重构时可能频繁失效;单独使用时看不到完整协作链
集成或服务级测试多个组件的协作,或服务的公开接口能覆盖路由、配置、序列化和业务编排等问题通常比单元测试慢、结构更复杂,也需要管理服务启动、测试数据与依赖
E2E 测试跨系统的关键用户旅程更接近真实使用,可验证完整流程通常数量最少,编写、运行和诊断成本较高,环境与外部依赖也更多

这些层级不是适用于所有团队的测试数量配方。金字塔表达的是一个默认判断:在当时的工程条件下,单元测试通常便宜、快速,适合成为广泛的基础;越接近真实使用,测试覆盖的协作面越大,代价也通常越高。

测试的成本,不只在运行时

Akehurst 把测试金字塔看作经济模型:每一类测试都在“可能揭示的信息”与“获得这些信息的代价”之间取舍。成本至少包括四部分:

  • 编写与维护测试所需的工作量;
  • 测试执行耗时;
  • 失败后定位根因所花的时间;
  • 代码重构后测试是否仍能验证用户可观察的行为。

金字塔形成时,计算机较慢,测试与调试工具不够成熟,开发基础设施也很笨重。在这样的条件下,范围窄、彼此隔离的单元测试容易运行和定位故障,往往更划算。

文章认为,后来的技术进步改变了这笔账。现在,通过公开接口快速运行大量测试更加可行,测试范围不必局限在狭窄的单元边界;测试框架降低了编写稳定、可维护集成测试的成本;调试器和可观测性工具让团队更容易从较宽的测试范围中定位问题,因此不必像过去那样依赖大量细粒度单元测试来获得快速反馈。加上 Mock 工具、应用框架和算力的进步,一些过去显得昂贵的服务级测试,可能带来更好的投入产出比。

“可能”很重要。工具进步不会自动消除环境不稳定、测试数据耦合、模拟行为过时或失败难以复现等问题。团队仍要测量自己的运行与维护成本。

WireMock 的选择:让测试组合的中间层更厚

Akehurst 介绍 WireMock 自己的测试代码库更像“花瓶”:中间较宽。类似形状也常被称为“奖杯”或“钻石”,但这些图形的测试分类和适用范围并不完全相同。

WireMock 的具体做法是保留单元测试,用它检查复杂逻辑或大量示例;更多回归测试则通过服务的公开 API 调用整个服务,并在进程之外模拟外部依赖。团队常使用 WireMock 自身来建立这些替身。这样的测试既经过真实服务入口,也不必依赖第三方环境始终在线、数据保持不变。

这类测试有几个实际收益:它把外部系统隔离在可控替身之后,测试执行不容易被外部服务拖慢;不依赖可能变化的第三方数据,也能减少由不可控外部因素引起的假失败;请求仍会经过与生产环境相近的路由、校验、业务编排和响应序列化,因此有机会发现更多服务协作问题;测试断言外部可见行为时,内部实现重构的空间也更大。Akehurst 进一步举例:理论上即使替换整个 Web 框架,这些测试仍可提供保护。

示例:订单服务收到创建订单的请求后,会向库存服务查询商品是否可售。下面的伪代码通过订单服务的公开 API 发起请求,用可控替身响应库存查询,再检查外部可见结果。

# Given:让外部库存依赖返回可控结果
inventory_stub.respond(
    method="GET",
    path="/inventory/sku-123",
    status=200,
    json={"available": True, "quantity": 5},
)

# When:通过订单服务的真实公开接口提交请求
response = order_service.post(
    "/orders",
    json={"sku": "sku-123", "quantity": 2},
)

# Then:断言业务结果和依赖调用
assert response.status_code == 201
assert response.json()["status"] == "CREATED"
assert inventory_stub.received("GET", "/inventory/sku-123")

这只是说明测试边界的伪代码,不是某个 WireMock 版本可直接运行的 API。实际测试还应覆盖库存不足、依赖超时、错误响应等分支;这些结果应由产品和服务契约决定。

奖杯、钻石和花瓶不是同一套分类标准

WireMock 把自己代码库的形状称作“花瓶”,并在原文中提到,读者也提出了“奖杯”和“钻石”的说法。测试奖杯通常归于 Kent C. Dodds;他回顾过,这个图形最初出现在 2018 年的一条推文中,也受到 Guillermo Rauch 关于“不要写太多测试、以集成测试为主”的观点启发。Dodds 的奖杯把静态检查放在底部,并主要讨论 JavaScript 单个代码库的测试分类;他也提醒,这套分类不能不加区分地套到微服务或所有后端架构上。

实际讨论时,测试分类的名称并不统一。不同作者所说的“单元测试”“集成测试”“服务测试”可能对应不同边界。讨论比例前,团队最好先说清楚测试启动了什么、跨过哪些进程或网络边界、依赖哪些真实服务,以及断言的是外部行为还是内部实现。否则,比例之争可能只是术语没对齐。

中间层变厚,不等于其他测试可以删掉

单元测试依然适合验证复杂定价、权限、状态机和边界组合。它们运行快、失败定位直接,尤其适合大量输入样例。API 测试不应为了追求“更接近生产”而重复覆盖每一个纯函数。

通过 Mock 隔离第三方服务,可以让反馈更稳定;但如果接口假设有误,测试也可能因此掩盖真实集成问题。需要用消费者契约、提供方验证,或定期的真实依赖集成测试,检查模拟行为是否仍与真实接口一致。关键调用还应覆盖异常、超时和重试边界。

服务级 API 测试也不能证明浏览器界面可用、键盘焦点顺序合理、辅助技术读屏正确,或用户能顺利完成登录和付款。为高价值用户旅程保留少量 E2E 测试,另外按风险安排可访问性、性能、安全和韧性验证,仍然必要。

按风险和反馈价值调整测试组合

可以从风险清单开始,而不是先给单元测试、API 测试和 E2E 测试分配目标百分比。对每个待选测试,评估风险覆盖、反馈速度、维护工作量、失败诊断时间和信号可信度。不要在没有校准的情况下把这些维度硬合成一个总分;它们是帮助团队说明取舍的依据。

风险示例可以优先验证的边界需要补充的证据
折扣规则的边界计算错误单元测试或属性测试通过服务 API 做关键业务回归
订单字段校验或状态流转错误服务级 API 测试契约检查与真实提供方联调
第三方库存接口超时或返回异常API 测试配合 Mock周期性真实集成与韧性检查
用户无法完成结账少量关键 E2E 测试认证、API 和部署后监控
浏览器交互或可访问性问题UI 与可访问性测试跨浏览器检查和人工体验评估

评估现有套件时,可以回看数个迭代:各类测试的执行时间和不稳定比例、实际发现的缺陷及严重程度、从失败到定位根因所花的时间、重构带来的维护投入,以及 Mock 曾掩盖哪些真实问题。然后选一个服务试点:保留复杂逻辑的单元测试,把部分脆弱、依赖内部实现的检查改成公开 API 行为测试,以契约和真实集成验证 Mock,再用少量 E2E 守住关键用户旅程。观察几个迭代后再决定是否推广。

把作者观点放回它的适用范围

Tom Akehurst 是 WireMock 的 CTO 和联合创始人。通过公开 API 测试整个服务、用 Mock 隔离外部依赖,正是 WireMock 产品关注的领域。这不代表他的论点就站不住脚,但读者仍应把工程原则和具体工具分开判断:模拟外部依赖可以使用 WireMock,也可以使用其他 Mock 服务、服务虚拟化或容器化测试环境。

原文把“花瓶”描述为 WireMock 自己的观察结果,不是目标或经验法则;它也承认不同项目会形成不同形状。作者甚至预测,旧式金字塔终将留在历史书里。这是他对趋势的判断,不代表每个团队都该把测试组合改成花瓶。更稳妥的做法是根据业务风险、反馈价值和维护成本选择测试边界,而不是为了图形调整比例。

参考资料

核查说明:WireMock 页面标注的发布日期是 2026-08-05,但原文的实践启示部分还出现“2025”字样。本文按页面日期标注来源,不据此处年份推断发布时间。Mock 与契约、E2E 及非功能测试的边界属于本文的 QA 实践补充。

分享