跳转至

测试左移:需求评审与风险评估

一、一次 30 分钟就「通过」的评审,返工了 5 天

2026-07 的退款需求评审,会议记录原文(节选):

10:00  PM     :需求就这些,支持退款,金额原路退回,大家看下有没有问题。
10:03  Dev    :没有,接口加个字段就行。
10:05  QA     :退款按钮放哪?
10:06  PM     :订单详情页,已支付状态显示。
10:08  QA     :好的,没问题。
10:30  PM     :那就这样,我发 JIRA 了。

评审结论只有三个字:「已评审」。5 天后联调暴露 3 个问题:

  1. 部分退款后,订单状态是「已退款」还是「部分退款」没人定义,前端按「已退款」展示,用户以为全额退了。
  2. 优惠券分摊金额没算,退款金额比用户实付多 10 元,测试环境跑了 12 笔才发现。
  3. 退款回调重复投递,退款流水写了两条,财务对账差 20 元。

返工成本:3 人 × 5 天 = 15 人日。评审如果按下面的清单走,这三个问题在 10:05 就能被问出来,成本是 1 小时。

左移不是多开会,是把「没写清楚的地方」在写代码前变成待办。

二、评审前 24 小时要做的准备

会议现场才开始读需求,必然只能读到「有没有错别字」。准备工作固定四件事:

时间点 动作 产出
T-24h 产品发出需求文档 + 原型 + 接口草稿,标注本次变更点 带 [变更] 标记的文档
T-24h 测试通读,按七维清单逐条打问号 问题清单(≥5 条,带原文行号)
T-12h 问题按「阻塞开发 / 影响测试设计 / 可后置」分级 分级后的问题列表
T-1h 把阻塞级问题提前发给产品,能异步解决的先解决 会上只剩需要多方拍板的

会前问题清单长这样,不要写「需求不清晰」:

Q1 [阻塞开发] 第 3 节「退款金额原路退回」:部分退款时优惠券分摊金额是否退回?
   涉及表 refund_order.amount 与 coupon.discount_amount,需要明确计算式。
Q2 [阻塞开发] 第 4 节:退款成功后订单状态取值?枚举目前只有 PAID/CLOSED,
   没有 PARTIAL_REFUNDED,需确认是否新增。
Q3 [影响测试] 第 5 节「异步通知」:重试次数与间隔未定义,无法设计幂等用例。
Q4 [可后置] 第 6 节文案「退款将在 1-3 个工作日到账」与旧版「24 小时」冲突。

需求里出现这些词,直接标为「不可测」,要求改写:

# 扫需求文档里的模糊词,输出行号,粘进问题清单
grep -nE '尽快|适当|合理|友好|尽量|等等|视情况|较快|较好的|一定程度|原则上' requirement.md

模糊词的判定标准:同一句话,两个测试人员能写出不同预期结果,就是不可测。 「界面友好」不可测;「列表加载超过 2 秒显示骨架屏」可测。

三、需求评审检查清单(七个维度)

每个维度都要有「检查问句 + 不合格示例 + 必须索取的产出」,否则清单会退化成勾选表演。

1. 可测性

检查问句 不合格示例 必须索取的产出
每条验收标准能否用「给定-当-则」写出来? 「退款体验要顺畅」 至少 1 条 Gherkin AC
预期结果是否是具体值/状态/文案? 「提示成功」 具体文案 + 状态码 + 字段值
有没有无法观测的内部行为被当成 AC? 「异步处理完成」 可查询的状态字段或消息日志
Feature: 订单部分退款

  Scenario: 部分退款金额按实付比例分摊
    Given 订单 SO20260701001 实付 90.00,其中券抵扣 10.00
    And 订单包含商品 A(60.00)与商品 B(40.00)
    When 对商品 A 发起退款
    Then 退款金额为 54.00(60.00 × 90.00 / 100.00,四舍五入到分)
    And 订单状态为 PARTIAL_REFUNDED
    And 优惠券不退回

Gherkin 写法本身就是可测性检查:写不出来,说明需求缺信息。

2. 边界

检查问句 不合格示例 必须索取的产出
金额的最小单位、最大额度、舍入规则? 「按实际金额退」 分/元单位 + 舍入方式 + 上限
退款次数、时间窗口? 「支持多次退款」 次数上限 + 时间窗
0 元订单、全额退、超额退怎么处理? 未提及 三种场景的预期

计算类需求的边界一定要落到公式:refund = round(paid × itemAmount / orderAmount, 2, HALF_UP),并明确「多次部分退款后尾差归谁」。这是资损高发点。

3. 异常

检查问句 不合格示例 必须索取的产出
支付渠道退款失败怎么办? 「失败重试」 重试次数/间隔/最终态
回调重复、乱序、丢失? 未提及 幂等键 + 乱序处理策略
退款中用户再次申请? 未提及 互斥规则 + 返回码
服务重启/消息积压? 未提及 补偿任务 + 对账频率

4. 权限

检查问句 不合格示例 必须索取的产出
谁可以发起退款?客服/运营/财务分别到什么额度? 「客服可以退」 角色-额度矩阵
越权退款如何拦截? 未提及 鉴权点 + 审批流
操作日志是否可追溯? 未提及 日志字段(操作人/工单号/金额)

5. 数据一致性

检查问句 不合格示例 必须索取的产出
退款单、订单、支付流水、券、库存、积分,哪些字段要同事务更新? 未提及 字段级一致性矩阵
对账口径是什么?多久跑一次?差异怎么处理? 「财务会核对」 对账文件字段 + 差异工单流程
部分成功(退 2 件成功 1 件)怎么记? 未提及 明细粒度 + 汇总规则

6. 兼容

检查问句 不合格示例 必须索取的产出
老版本客户端收到新状态枚举会怎样? 未提及 枚举兜底规则
历史订单没有退款字段,展示如何兜底? 未提及 数据回填脚本或默认值
接口是否 breaking change? 「加个字段而已」 OpenAPI diff 结果
# oasdiff v1.11+,检测接口破坏性变更
oasdiff breaking old-openapi.yaml new-openapi.yaml
1 breaking change(s) detected:
  POST /api/v1/orders/{orderNo}/refund
    - request property "amount" changed from string to number

这类变更不提前发现,老客户端上线即报 JSON parse error。

7. 回滚

检查问句 不合格示例 必须索取的产出
退款上线后出现资损,怎么停? 「回滚代码」 开关名 + 生效范围 + 关闭耗时
已产生的退款数据要不要回滚? 未提及 数据修复 SQL 或补偿任务
回滚后新老版本数据兼容? 未提及 双写/兼容期说明

评审通过的最低标准:七维中「可测性、边界、异常、数据一致性」四维不得有阻塞级空白;其余三维允许带待办,但必须有 owner 和截止日期。

四、把一段退款需求拆成测试点

需求原文(真实文档节选,歧义原样保留):

支持对已支付订单发起退款,退款金额原路返回。支持部分退款,按商品维度退。 退款成功后订单状态同步更新,并通知用户。客服可在后台操作,金额较大的需要审批。 退款失败时自动重试,最终失败进入人工处理。优惠券按规则处理。

逐句拆解:

# 原文 歧义/缺口 必须问的问题 测试点 风险
1 已支付订单发起退款 已发货/已完成能退吗?退款中能再退吗? 哪些状态允许退款 状态准入矩阵(PAID/SHIPPED/COMPLETED/CLOSED × 退款) 高
2 退款金额原路返回 原路是哪个渠道?混合支付(券+余额+微信)怎么分? 多支付方式拆分规则 混合支付退款金额分配、渠道退款单号回填 极高
3 支持部分退款,按商品维度退 数量维度?同商品分两次退? 退款粒度与次数上限 单品/多品/同一商品部分数量/多次退款累计 高
4 退款成功后状态同步更新 部分退款的状态枚举未定义 新增状态值与展示文案 PARTIAL_REFUNDED 的状态流转与列表筛选 高
5 通知用户 渠道(短信/推送/站内信)与失败补偿 通知模板、重试 通知发送与去重、通知失败不影响退款主流程 中
6 客服后台操作 权限边界、可退额度 角色-额度矩阵 越权、超额度、跨商户操作 极高
7 金额较大需要审批 「较大」未量化 阈值 + 审批人 阈值边界(9999.99/10000/10000.01)、审批拒绝后状态 高
8 失败自动重试 次数、间隔、超时、幂等 重试策略 重复回调只退一次、重试达上限转人工、乱序回调 极高
9 最终失败进入人工处理 人工处理入口与状态 工单字段 人工补退、人工关单、对账差异 中
10 优惠券按规则处理 规则未定义;部分退款是否退券 券分摊与退回规则 全额退退券/部分退不退券/券已过期 极高

10 行里有 6 个「必须问」,这就是评审要产出的东西。没有这 6 个答案,用例设计只能靠猜,猜错的概率和资损金额成正比。

把答案写成可执行 AC(示例 2 条):

Scenario: 金额等于审批阈值时不触发审批
  Given 客服角色额度上限为 10000.00
  When 发起退款金额 10000.00
  Then 退款单直接进入 PROCESSING
  And 不生成审批工单

Scenario: 金额超过阈值 1 分触发审批
  Given 客服角色额度上限为 10000.00
  When 发起退款金额 10000.01
  Then 生成审批工单,状态为 PENDING_APPROVAL
  And 退款不发起渠道请求

五、风险矩阵:先评分,再决定投入

概率 P 与影响 I 都取 1~5,得分 = P × I。矩阵如下(分数写在格子里):

影响 \ 概率 1 极低 2 低 3 中 4 高 5 极高
5 灾难(资损/合规) 5 10 15 20 25
4 严重(核心流程阻断) 4 8 12 16 20
3 中等(功能受损有绕过) 3 6 9 12 15
2 轻微(体验受损) 2 4 6 8 10
1 可忽略(文案) 1 2 3 4 5

分档规则(写死,避免每次评审重新吵):

分值 等级 处置
15~25 极高 必须有专项测试(安全/性能/故障注入)+ 灰度 + 回滚演练,测试负责人签字
10~14 高 全量用例 + 自动化回归 + 上线后 24 小时观察
5~9 中 常规用例 + 变更点回归
1~4 低 抽测或随版本验证

附加规则:影响 I=5 时,无论概率多低,等级不得低于「高」。资损类问题不接受「概率低所以不测」。

退款需求的评分示例:

风险项 P I 分值 等级 对应动作
混合支付退款金额拆分错误 4 5 20 极高 专项用例 18 条 + 财务对账演练
重复回调导致重复退款 3 5 15 极高 幂等测试 + 故障注入(重放 100 次)
审批阈值边界判断错误 3 4 12 高 边界用例 6 条 + 权限矩阵
部分退款状态展示错误 4 2 8 中 前端用例 + 老版本兼容
退款通知文案不符 3 1 3 低 抽测

评分表可以直接用表格工具算,公式(假设 P 在 B 列、I 在 C 列):

=IF(OR(C2=5,B2*C2>=15),"极高",IF(B2*C2>=10,"高",IF(B2*C2>=5,"中","低")))

注意 OR 分支要放最前,否则 I=5、P=1 的项会被算成「中」。

六、FMEA:S/O/D 与 RPN 计算

风险矩阵看「需求整体」,FMEA 看「单个失效模式」。三个维度都取 1~10:

维度 含义 1 分 5 分 10 分
S 严重度 失效后果 无感知 功能受损可绕过 资损/数据丢失/合规事故
O 发生度 失效发生概率 几乎不发生(<1/10000) 偶发(1/1000) 高频(>1/10)
D 探测度 现有手段发现难度 必现且监控可即时告警 需要专项用例才能发现 上线后靠用户投诉

RPN = S × O × D,取值 1~1000。处置阈值:RPN ≥ 100,或 S ≥ 8,必须制定预防/探测措施;措施后重新评分,记录改善值。

退款需求 FMEA 示例:

失效模式 后果 S O D RPN 措施 措施后 S/O/D 新 RPN
混合支付拆分比例算错 多退/少退 9 4 6 216 用实付比例计算 + 财务口径 AC + 对账断言 9/2/3 54
回调重放重复退款 重复出款 10 3 7 210 退款单号做唯一键 + 状态机幂等 10/1/2 20
部分退款状态未同步 用户重复申请 6 4 5 120 同事务更新 + 状态一致性巡检 6/2/2 24
审批阈值用 > 而非 >= 该审批未审批 8 3 4 96(S≥8 仍需处理) 边界用例 + 单元测试参数化 8/1/2 16
通知失败阻断主流程 退款卡单 7 3 3 63 通知异步化 + 失败落表 7/2/2 28

O 和 D 不能拍脑袋,要有数据来源:

  • O 用历史缺陷数/调用量估算。例:退款相关缺陷近 12 个月 14 个,退款调用 2.1 万次 → 约 1/1500,对应 O=5 附近。
  • D 用现有用例与监控覆盖反推。有自动化断言且告警覆盖 = 2~3;只能手工专项测 = 5~6;线上投诉才发现 = 8~10。

常见误用:

误用 后果 正确做法
所有项 O 都填 5、D 都填 5 RPN 全部集中在 125,没有区分度 O 用历史数据,D 用覆盖证据,并在表格里写依据
只看 RPN 排序 高 S、低 RPN 的资损项被排到后面 加 S≥8 的一票否决规则
措施写「加强测试」 无法验证是否生效 措施必须是可验证动作:新增哪条用例/哪个唯一键/哪个告警
评完不更新 半年后阈值失效 每次需求变更或线上故障后复评,保留历史版本

七、需求变更影响分析

开发第 3 天提出变更:「产品说要支持部分退款,接口加个 items 数组」。

变更影响分析表(评审必填):

变更点 受影响模块 关联用例 需回归范围 工作量 风险 决策
退款接口支持按商品明细退 refund-service / order-service / 财务对账 TC-RF-01~18 退款全流程、订单金额、对账 3 人日 极高 本迭代做,砍掉「退款通知模板改版」
订单状态新增 PARTIAL_REFUNDED 订单列表/详情/筛选/报表 TC-OD-22~31 订单查询、导出、BI 报表 2 人日 高 本迭代做,需数据回填脚本
券分摊金额计算 coupon-service TC-CP-40~46 券核销、退款、对账 2 人日 极高 本迭代做,财务确认口径
后台审批流 admin-web 无(新功能) 权限、审批 3 人日 高 延期到下迭代,先上线人工工单

找出「改了哪些代码」不能靠回忆:

# 本次变更涉及的文件
git diff --name-only origin/release/2.3.0...HEAD

# 退款相关历史变更,判断影响面
git log --oneline --since="6 months ago" --grep="refund" -30

# 具体改动量
git diff --stat origin/release/2.3.0...HEAD -- src/refund src/order
 src/refund/RefundService.java        | 142 ++++++++++++++++-----
 src/refund/RefundCalculator.java     |  88 ++++++++++---
 src/order/OrderStatus.java           |  12 ++-
 3 files changed, 198 insertions(+), 44 deletions(-)

变更评审只问三句:

  1. 改了什么:字段、枚举、状态机、接口签名,逐项列出。
  2. 不改什么:明确本次不动的范围,防止「顺手重构」扩大回归面。
  3. 怎么验证:新增哪些用例、回归哪些旧用例、谁来执行、什么时候能测。

第三问没有答案,变更就不该进本迭代。

八、左移在各阶段的动作与产出物

阶段 测试动作 产出物 门禁(不通过会怎样)
需求 七维清单评审、AC 可测性检查、风险矩阵 问题清单、AC 列表、风险等级 阻塞级问题未闭环,需求不进开发排期
设计 评审接口契约、状态机、数据模型、异常码 OpenAPI diff、状态迁移表、错误码表 breaking change 未声明,接口不准提测
编码 单测覆盖增量、静态扫描、契约测试 覆盖率报告、Sonar 结果 增量覆盖率 <60%,CI 卡住合并
提测 冒烟、准入检查、环境校验 冒烟报告、提测单 冒烟失败,构建打回
发布 回归、灰度、回滚演练、监控确认 测试报告、发布检查单 P0/P1 未清零,禁止发布
线上 观察黄金指标、逃逸缺陷登记 24 小时观察记录、逃逸分析 指标越界,触发回滚

每一条都要有工具落地,不能只写「加强」:

# 编码阶段:单测 + 覆盖率门槛,CI 里执行
mvn -q test jacoco:check -Djacoco.minimum=0.60

# 契约阶段:接口变更检测
oasdiff breaking old.yaml new.yaml

# 提测阶段:冒烟门禁,失败直接非零退出
pytest -m smoke --maxfail=1 -q || exit 1

九、评审会怎么开

时间盒(60 分钟)

时长 内容 产出
5 分钟 产品讲背景与本次变更点,不讲全文 变更清单确认
40 分钟 逐条过会前问题清单,每条不超过 3 分钟 决议:通过/修改/待定
10 分钟 过风险矩阵,确认测试策略与资源 风险等级与测试投入
5 分钟 确认待办、owner、截止时间 会议纪要

超过 60 分钟还没结论的条目,一律转为「会后专项」,不占用全场时间。

角色分工

角色 必须回答的问题
产品 AC 是什么?异常场景的业务预期是什么?优先级怎么排?
开发 涉及哪些表和服务?有无 breaking change?异常码怎么定义?
测试 哪些点不可测?需要什么数据/权限/工具?风险最高的三项是什么?
架构/运维 有无容量与依赖风险?回滚路径是什么?监控覆盖哪些指标?

输出模板(可直接复制)

# 需求评审纪要:订单部分退款(v1.2)

## 结论
- [ ] 通过(可进入开发)
- [x] 有条件通过:3 个阻塞问题需在 07-15 前闭环
- [ ] 不通过

## 阻塞问题
| # | 问题 | Owner | 截止 | 状态 |
| --- | --- | --- | --- | --- |
| 1 | 混合支付退款拆分规则 | PM | 07-12 | 已闭环 |
| 2 | 部分退款状态枚举与展示 | PM + 前端 | 07-13 | 进行中 |
| 3 | 券分摊金额计算式 | 财务 | 07-15 | 未开始 |

## 风险与测试策略
| 风险 | 等级 | 策略 |
| --- | --- | --- |
| 重复退款 | 极高 | 幂等专项 + 回调重放 100 次 |
| 金额拆分 | 极高 | 18 条专项用例 + 对账演练 |

## 待办
- [ ] 开发补充错误码表(Owner:@dev-li,07-16)
- [ ] 测试补充退款专项用例(Owner:@qa-wang,07-17)

评审有效性怎么度量

指标 公式 目标 数据来源
需求缺陷密度 评审发现问题数 / 需求页数 ≥1.5 条/页 评审纪要
评审逃逸率 开发阶段才发现的歧义数 / 评审问题总数 ≤20% JIRA label=需求评审
变更返工率 因需求不清导致的返工任务数 / 总任务数 ≤5% 迭代看板
评审闭环率 截止前闭环的阻塞问题数 / 阻塞问题总数 100% 纪要待办
project = QALAB AND labels = "需求评审" AND created >= startOfQuarter()
project = QALAB AND labels = "返工" AND text ~ "需求未定义"

如果评审逃逸率连续两个迭代 >20%,问题不在测试,而在问题清单没有分级、阻塞项没有截止日期。

十、检查清单

  • 会前 24 小时拿到文档、原型、接口草稿,变更点有标记
  • 会前问题 ≥5 条,带原文行号,已按阻塞/影响/后置分级
  • 七维清单逐条过,重点确认可测性、边界、异常、数据一致性
  • 每条 AC 能写成 Given/When/Then,且有具体数值或状态
  • 金额类需求写明单位、舍入方式、尾差归属
  • 状态机新增状态时,确认枚举兜底与历史数据展示
  • 接口变更跑过 oasdiff,breaking change 已声明
  • 权限需求有角色-额度矩阵,越权有明确返回码
  • 风险矩阵评分有依据,I=5 的项不低于「高」
  • FMEA 只对高 S 或高 RPN 项做,措施可验证
  • 变更影响分析表填了回归范围与工作量
  • 评审纪要含结论、阻塞项、owner、截止时间
  • 阻塞项未闭环时,需求不进入开发排期

相关阅读