软件项目开发全流程 · 第 19 课 · 测试全景:测试金字塔
1、上线前一晚的手忙脚乱
周五晚上 11 点,团队准备上线。测试小张在群里喊:"我点了下'忘记密码',页面 500 了!"
开发小李改到凌晨 1 点,修好了"忘记密码"。结果第二天用户反馈:"登录"也不好使了——因为修复时碰到了共用的密码校验逻辑。
这就是没有测试体系的典型:靠人肉点,点不全,改一处崩另一处。
本课解决:
- 测试到底在测什么?分几类?
- 为什么是"金字塔"而不是"平铺"?
- 一个团队该投多少精力在哪种测试上?
2、测试在测什么:三个层次的问题
测试回答的是不同层次的问题:
| 层次 | 问题 | 谁关心 |
|---|---|---|
| 代码正确性 | 这个函数,输入 X 是不是输出 Y? | 开发者 |
| 组件协作 | 这些模块拼起来,数据还对不对? | 开发者 / 测试 |
| 用户价值 | 用户能不能顺畅完成"下单"这件事? | 产品 / 测试 / 用户 |
只测最后一层(人肉点页面):慢、贵、覆盖不全。
只测第一层(只写单元测试):快,但拼起来可能还是坏的。
都要,但比例不同——这就是金字塔。
3、测试金字塔
测试金字塔(Test Pyramid) 是 Mike Cohn 提出的分层模型:
▲
╱ ╲ 端到端测试(E2E)
╱───╲ 少、慢、贵、接近真实用户
╱ ╲
╱───────╲ 集成测试(Integration)
╱ ╲ 中等数量、验证模块协作
╱───────────╲
╱ ╲ 单元测试(Unit)
╱───────────────╲ 多、快、便宜、隔离核心原则:
越往下的测试越多、越快、越便宜;越往上的越少、越慢、越贵。
为什么不能倒过来("冰淇淋甜筒"反模式)?
╲ ╱ ← 大量 E2E:跑 2 小时、经常 flaky、
╲ ╱ 改一个按钮就红一片,没人敢信
╲─────────╱
╲ ╱ ← 少量单元:本该最快发现的问题,
╲ ╱ 却要等到跑完 E2E 才知道
╲ ╱
╲_╱甜筒反模式的代价:反馈慢(要等几十分钟)、定位难(红了不知道哪坏了)、脆弱(UI 一改全崩)。团队最后会不信任测试,直接忽略红灯——测试就白写了。
4、每一层测什么、怎么写
① 单元测试(Unit Test)
测:一个函数 / 一个类,隔离掉外部依赖(数据库、网络)。
def test_calc_discount_at_100_returns_10_percent():
assert calc_discount(100) == 10.0
def test_calc_discount_below_threshold_returns_zero():
assert calc_discount(50) == 0.0特点:毫秒级,几百上千个也能秒跑完。这是金字塔的底座,数量最多。
② 集成测试(Integration Test)
测:多个单元协作是否正确——如"业务逻辑 + 真实数据库"、"服务 A 调服务 B"。
def test_create_order_writes_to_database(db):
order = create_order(db, user_id=1, items=[...])
row = db.query("SELECT * FROM orders WHERE id=?", order.id)
assert row is not None特点:比单元慢(要起数据库/容器),但能发现"接口对不上""事务没提交"这类问题。
③ 端到端测试(E2E / UI Test)
测:从用户视角,走完整流程——打开浏览器、点击、填表、提交、看结果。
def test_user_can_reset_password(page):
page.goto("/login")
page.click("text=忘记密码")
page.fill("#email", "a@b.com")
page.click("text=发送")
assert page.text_content(".notice") == "邮件已发送"特点:最接近真实、最能给信心,但最慢、最贵、最容易碎。所以只覆盖最关键的"黄金路径"(登录、下单、支付……)。
5、测试策略:把金字塔写下来
团队应该有份一页纸的测试策略,回答:
- 哪些层我们用?各占比多少?(如 70% 单元 / 20% 集成 / 10% E2E)
- 关键路径是哪些?(这些必须有 E2E)
- "完成"要不要测试?(Definition of Done,第 17 课)
- CI 里跑哪些、多久跑一次?(第 16 课)
- 覆盖率的红线是多少?(如新代码 ≥ 80%)
一个务实的参考比例(不是铁律):
单元测试 ~70% 快,覆盖逻辑细节
集成测试 ~20% 验证协作与数据
E2E 测试 ~10% 守关键业务路径别迷信百分比:重点是"快的多、慢的少,关键路径别漏"。团队舒服、能长期坚持,比数字漂亮重要。
6、更全的测试类型地图
金字塔是按"粒度"分层的。按"测什么性质"还有另一维度:
| 类型 | 测什么 | 例子 |
|---|---|---|
| 功能测试 | 功能对不对 | 登录能成功 |
| 回归测试 | 改完有没有弄坏老功能 | 修了密码,登录还正常吗 |
| 冒烟测试 | 最基本功能是否可用 | 部署后能打开首页吗 |
| 性能测试 | 快不快、扛不扛得住 | 1000 并发响应时间(第 23 课) |
| 安全测试 | 有没有漏洞 | SQL 注入、XSS(第 23 课) |
| 探索式测试 | 人主动"找茬" | 测试员凭经验乱点、找边界 |
| 可用性测试 | 好不好用 | 用户能不能找到按钮 |
探索式测试不能省:自动化测试只能验证"你想到的",探索式测试靠人的创造力发现"你没想到的"(第 6 课的用户视角)。两者互补,不是替代。
7、常见误区
| 误区 | 问题 | 正解 |
|---|---|---|
| 只写 E2E(甜筒) | 慢、脆、贵,难维护 | 补底层单元测试,E2E 只守关键路径 |
| 追求 100% 覆盖率 | 又累又没收益,测的是"没用的代码" | 关注关键逻辑覆盖,别为数字而测 |
| 测试写完就不管 | 测试腐化,最后全红 | 测试也是代码,要重构、要维护 |
| 测的是实现不是行为 | 改个内部写法测试就崩 | 测"对外行为",别测私有实现细节 |
| 手工测试全靠人 | 重复劳动、易漏 | 回归用自动化,人做探索式 |
一句话立场:测试的目的不是"覆盖率数字",而是"改动时的信心"——敢不敢在周一改动核心代码然后放心上线?如果敢,测试就是有效的。
8、动手实验 🎯
任务:为贯穿项目「团队任务清单」设计一份测试策略。
- 列出 10 个核心功能(注册、登录、建任务、改任务、删任务、分配、标记完成、筛选、搜索、统计)。
- 分层归位:每个功能,标注"单元 / 集成 / E2E"各要测什么,形成一张表。
- 写出关键路径(必须有 E2E 的 3 条),并说明理由。
- 定比例:给出你团队的金字塔比例,算出 100 个测试怎么分。
- 写策略文档(半页),包含"占比 / 关键路径 / DoD / CI 跑法 / 覆盖率红线"。
- 对照反模式:检查你的方案有没有掉进"甜筒",如有则调整。
- 反思:如果只能保留一层测试,你保留哪层?为什么?
下一课深入金字塔底座——单元测试与 TDD,并给出红-绿-重构的完整实操。
9、今天你带走的三句话
- 测试分层:单元多而快、E2E 少而慢;倒过来的"甜筒"会让测试失去信任。
- 测试的目的是"改动的信心",不是覆盖率数字。
- 自动化 + 探索式互补:机器测你想到的,人找你想不到的。
下一课:单元测试与 TDD。