搞测试这行干得久了,你会发现一个规律:需求文档里真正难缠的往往不是单个字段怎么取值,而是多个条件搅在一起时,系统到底该走哪条路。我早年在一个电商后台项目上就栽过跟头——积分兑换的规则看着不复杂,测试用例也按等价类和边界值铺得满满当当,结果上线后用户反馈“有欠款也能兑换成功”,后台一查,兑换接口根本没校验用户的未结清欠款状态。问题就出在,我当时只测了“积分够、有货、成功兑换”这条主路径,把“积分够、有货、但人有欠款”的组合给漏了。
那次之后我认真补了因果图分析法,越用越觉得,这才是对付多条件耦合场景的“正规军”。它不像等价类边界值那样只盯着单个输入的取值范围,而是把输入条件(原因)和输出结果(结果)之间的逻辑关系画成一张图,再转成判定表,最终生成一套组合覆盖度很高的测试用例。对测试人员、需求分析人员、甚至开发自测来说,这玩意儿都能帮你把需求里的“隐含逻辑”给逼出来。这篇文章我就把因果图分析法的完整用法掰开揉碎讲清楚,从符号、画法、判定表,到实际案例和踩坑经验,一次说透。
1. 大多数测试设计方法,并没有真正解决“条件组合”的问题
1.1 等价类和边界值为什么在这里失灵
等价类划分和边界值分析,本质是在解决“单个输入条件怎么取值”的问题。比如积分是多少、库存还剩几件、用户是哪类会员,这些都可以切等价类、找边界。但真实业务很少只有一个条件——积分够了不代表能兑换,还要看库存、看账户状态、看是不是会员。一旦条件变多,条件之间的组合关系才是决定系统行为的关键,而单条件设计方法对这部分几乎是盲区。
举个最直观的例子:一个兑换功能的输入条件有4个布尔量,每个条件只有“成立/不成立”两种取值,那么理论上就有2的4次方等于16种组合。等价类和边界值顶多告诉你“积分为0要测、积分999要测、积分1000要测”,但它们没法回答“积分够但库存为0且用户有欠款时,系统该提示什么”这个问题。你要是拍脑袋凭经验补几条组合用例,漏掉一两个分支太正常了。我当年那个线上故障,本质上就是组合空间没有被系统性地枚举出来。
1.2 因果图分析法到底解决什么问题
因果图分析法(Cause-Effect Graph Analysis)是典型的黑盒测试方法,它的核心是把需求规格中“原因”(输入条件)和“结果”(输出或状态变化)之间的逻辑依赖关系,用一张图显式地画出来。画完之后,再根据这张图生成判定表,判定表里每一列就是一种原因组合,对应一组输出结果,最后把判定表的每一列翻译成可执行的测试用例。
这套流程解决了三个问题:第一,它强迫你把需求里的每个条件、每个结果都拆出来,不拆就得不出完整的点;第二,它用逻辑门和约束符号把条件之间的“且、或、非、互斥、要求、屏蔽”关系全部显性化,需求和代码里那些说不清道不明的隐含规则才会暴露出来;第三,判定表能系统化枚举所有组合,避免靠“灵感”补用例。
很多人问,因果图和判定表是不是两个方法?严格说,因果图是分析工具,判定表是表达工具,两者是上下游的关系。你可以不画因果图直接硬列组合,但条件一多,漏画一条边、漏考虑一个互斥就麻烦了。我的习惯是:复杂逻辑先画因果图理顺,再落判定表;简单逻辑(比如两三个条件)直接列判定表就够,没必要上全套。
1.3 什么场景下优先用因果图
不是所有需求都值得上因果图。如果需求只是一堆独立入参的校验规则,每个参数各管各的,没有任何交叉逻辑,等价类加边界值就够了,画因果图纯属给自己加戏。但下面这几类场景,我建议你优先考虑:
- 条件数量适中(3到6个),且条件之间存在明显的组合依赖关系;
- 需求里出现“如果……并且……那么……”“当……或者……时”这类复合句式;
- 同一个功能点有多个不同的输出结果,比如提示信息、跳转页面、状态变更;
- 历史项目里出过“组合条件漏测”的线上故障。
条件数量超过6个时,我不太建议直接画纯因果图,因为2的n次方爆炸太快,一张图会塞得密密麻麻。遇到这种场景,更务实的做法是先用判定表按“有效组合”而不是“全组合”来收敛,或者配合正交实验、Pairwise法来做组合覆盖。因果图在这种场景里反而更适合作为需求沟通工具,画给产品和开发看,帮大家把逻辑对齐。
2. 因果图的基础构件:节点、逻辑门、约束符号
2.1 从需求里拎出“原因”和“结果”
画因果图第一步不是画线,而是拆节点。我习惯拿需求文档逐句过,把每个“能明确判定真假的陈述句”拎出来。所谓原因,就是系统接收到的输入状态,比如“用户点击了提交按钮”“订单金额大于等于100元”“用户是VIP会员”,它们都能用“成立/不成立”来判定。所谓结果,就是系统对外表现出的输出或状态,比如“页面提示登录成功”“订单状态变为已支付”“向用户发送短信通知”。
这里有个很容易犯的错:把“用户名为空”拆成两个原因。其实一个条件“用户名为空”本身就有成立和不成立两个取值,它是单一原因,不需要拆。真正需要拆的是那些在需求里分开描述、分开校验的独立条件。我一般会写一张“原因结果清单”,列成两列,给每个节点编号,比如原因1、原因2,结果1、结果2。编号有个好处,画图时不用拖着长句子,连线清爽很多。
2.2 四种逻辑关系怎么画、怎么读
因果图的逻辑关系,本质上就是布尔逻辑,一共四种:恒等、非、或、与。
- 恒等:原因成立,结果成立;原因不成立,结果不成立。这是最简单的一条直线,比如“用户勾选同意协议”对应“协议同意状态为真”;
- 非:原因成立,结果不成立;原因不成立,结果成立。在连线上画个小圆圈表示取反,比如“用户未登录”对应“不能进入个人中心”;
- 或:多个原因中只要有一个成立,结果就成立。符号是一个开口的扇形,类似电路图里的或门;
- 与:多个原因必须全部成立,结果才成立。符号是一个半圆弧收口的形状,类似与门。
画图时我一般不追求把符号画得特别标准,纸笔或者在线白板能看懂就行,但逻辑不能含糊。比如“提交订单并完成支付后才能进入发货流程”这句话,拆出来就是“提交订单”和“完成支付”两个原因,通过“与”关系指向“进入发货流程”这个结果。如果你发现一个结果可以由多个不同的原因组合触发,别急着画一堆平行的与门,先看看能不能用中间节点收敛一下,这是让图保持可读性的关键。
2.3 五种约束符号详解
原因和原因之间、结果和结果之间不是相互独立的,现实中它们往往有约束。因果图用五种约束符号来表示:
| 符号 | 名称 | 含义 | 典型场景 |
|---|---|---|---|
| E | 互斥 | 两个原因不能同时为真,最多只能有一个成立 | “支付方式为支付宝”和“支付方式为微信支付”互斥 |
| I | 包含 | 多个原因中至少有一个为真 | 多选条件里“邮箱”和“手机号”至少填一个 |
| O | 唯一 | 多个原因中有且仅有一个为真 | 订单状态在“待支付/已支付/已取消”中只能有一个 |
| R | 要求 | 原因A成立时,原因B必须也成立 | “选择信用卡支付”要求“填写有效期” |
| M | 屏蔽 | 原因A成立时,原因B必然不成立 | “系统维护中”屏蔽掉所有正常业务操作 |
这里我要多说一句M约束,它在实际项目中特别容易被忽略。很多测试用例设计者只盯着正常逻辑,忘了“前置屏蔽条件”。比如一个系统处于维护模式时,不管你的积分、库存、欠款状态是什么,所有兑换动作都必须被拦下来,这就是典型的屏蔽关系。画因果图时把这类约束画出来,后面生成判定表时能直接通过约束排除掉一大批“物理上不可能”的原因组合,组合空间瞬间小很多。
2.4 中间节点:让复杂图不至于变成毛线团
当原因和结果之间的逻辑链很长时,直接连线会让图变成一团乱麻。比如“积分够”和“是会员”先组成“有兑换资格”,这个“有兑换资格”再去和“有库存”“无欠款”组成“可兑换成功”——如果每次都要把四个原因直接连到最终结果,图上的线会交叉得没法看。所以我会在链路的中间引入中间节点,取名“有兑换资格”“满足前置条件”,它们像函数里的中间变量一样,本身不是需求里见的输入或输出,纯粹是为了整理逻辑结构。
引入中间节点还有一个额外的好处:它可以帮你和开发对“子条件组合”的认知。如果你在需求评审时能指着中间节点问一句“这里资格判断是且还是或”,很多歧义当场就被消掉了。我遇到过不少次,需求里写“会员且积分足够可进入兑换流程”,但开发实际代码写的是“会员或积分足够”,这种问题画图时一问一个准。
3. 完整实操:会员积分兑换系统的因果图推导
3.1 需求原文与原因结果拆分
下面我用一个真实的会员积分兑换场景,把因果图完整走一遍。需求原文大概是这样的:
只有注册会员,且积分不低于1000分时,用户才能发起积分兑换。兑换商品的库存必须充足;若账户存在未结清欠款,则即使其他条件都满足,也不允许兑换,系统提示“存在未结清欠款”。未注册用户提示“请先注册会员”;积分不足时提示“积分不足”;商品库存不足时提示“商品库存不足”。
这段需求看着不长,但逻辑嵌套不少。我先把原因和结果拆出来:
| 节点类型 | 编号 | 条件描述 | 取值 |
|---|---|---|---|
| 原因 | a | 用户是注册会员 | 是/否 |
| 原因 | b | 用户积分≥1000 | 是/否 |
| 原因 | c | 兑换商品库存充足 | 是/否 |
| 原因 | d | 账户无未结清欠款 | 是/否 |
| 中间节点 | e | 满足会员与积分条件 | a且b |
| 中间节点 | f | 满足全部兑换前置条件 | e且c且d |
| 结果 | x | 兑换成功,扣减积分 | 在f成立时出现 |
| 结果 | y | 提示“请先注册会员” | 在a不成立时出现 |
| 结果 | z | 提示“积分不足” | 在a成立且b不成立时出现 |
| 结果 | w | 提示“商品库存不足” | 在a、b成立且c不成立时出现 |
| 结果 | v | 提示“存在未结清欠款” | 在a、b、c成立且d不成立时出现 |
拆完之后你是不是已经嗅到等价类覆盖不到的味道了?对,这个需求里真正决定行为的不是“积分是100还是999”,而是“条件之间的排列组合”。你如果不画因果图,纯靠对着需求读,可能只会读出一条成功路径和几条独立失败路径,很难意识到“未注册用户”其实和“积分”“库存”“欠款”这三个条件是组合关系——它们的取值在未注册分支里压根不影响结果。
3.2 从原因到结果:逐步画因果图
画因果图时,我的习惯是先画“主干”,再补“分支”,最后加约束。
主干逻辑是这样的:原因a和原因b通过“与”门,得到中间节点e(满足会员与积分条件);e和c、d再通过一个“与”门,得到中间节点f(满足全部兑换前置条件);f往右连到结果x(兑换成功)。这四段是需求里最正的逻辑链,因果图最粗的一条路径。
接下来补分支。需求里说“未注册用户提示先注册”,这是取原因a的“非”,直接连到结果y。注意,这一段不用再和b、c、d做什么组合,因为a不成立时,其他条件在逻辑上已经没有意义了。然后是“积分不足”分支,触发条件是a成立且b不成立,所以a先和b的非做“与”,再去连结果z。同理,“库存不足”是a成立、b成立、c不成立,即a、b和c的非做与,连到结果w。“欠款”分支则是a、b、c成立且d不成立,连到结果v。
把这些逻辑用逻辑门表达出来,就是下面这组式子:
- e = a 与 b
- f = e 与 c 与 d
- x = f
- y = 非 a
- z = a 与 非 b
- w = a 与 b 与 非 c
- v = a 与 b 与 c 与 非 d
画图时注意:结果y、z、w、v、x这五个节点彼此之间是互斥的,也就是说一次操作只能命中原结果之一,所以要在结果节点之间加上E约束。这里还有个容易忽略的点:结果x和结果v其实不可能同时出现,因为有d和无d天然对立,但加上E约束能更直观地告诉读图的人“这些输出是互斥分支”。
3.3 引入约束,剔除不可能的原因组合
节点拆完、逻辑线画完,就要开始做“约束排查”。这一步是把因果图从“描述逻辑”变成“可用测试设计”的关键节点。
先看原因之间的约束。a是“注册会员”,b是“积分≥1000”。严格来说,积分是注册会员体系里的一个属性,所以当a不成立时,b的取值在实际系统中根本不存在——你不会在一个未注册账号上谈论它积分够不够。所以我在分析时会把“a=0且b=1”这类组合标记为“不可能组合”,或者叫“无意义组合”。这里要特别解释一下:无意义不代表测不到,而是这类组合在真实业务中触发不了,硬造出来的测试数据反而会误导结果判断。
再看结果之间的约束。前面提过,五个结果互斥,所以在判定表里每一行只能有一个结果为真。这个约束排除了“提示积分不足同时兑换成功”这种荒谬组合。
最后是屏蔽逻辑。如果系统处于维护中,那不管a、b、c、d怎么组合,所有业务结果都不会出现,只会看到“系统维护中”。这个我在这个案例里没有当作独立原因写进去,但如果真实场景确实有维护开关,就必须把“系统维护中”作为原因加进去,并给所有结果加M约束。这是很多测试新手容易漏的地方,我建议每次画完图强制问自己一句:有没有什么全局开关会屏蔽这些结果?
3.4 判定表的生成与化简
因果图画完,下一步是把图里的所有逻辑关系转成判定表。判定表的基本结构是:左边列出原因和中间节点,右边每一列是一种组合,下方是结果。
四个原因,每个原因取“1/0”(成立/不成立),笛卡尔积一共16种组合。我直接把这16种列出来,再用因果图里的逻辑关系算出每个中间节点和结果节点应该是什么状态:
| 序号 | a | b | c | d | e | f | 结果 |
|---|---|---|---|---|---|---|---|
| 1 | 0 | 0 | 0 | 0 | 0 | 0 | y(未注册) |
| 2 | 0 | 0 | 0 | 1 | 0 | 0 | y(未注册) |
| 3 | 0 | 0 | 1 | 0 | 0 | 0 | y(未注册) |
| 4 | 0 | 0 | 1 | 1 | 0 | 0 | y(未注册) |
| 5 | 0 | 1 | 0 | 0 | 0 | 0 | y(未注册) |
| 6 | 0 | 1 | 0 | 1 | 0 | 0 | y(未注册) |
| 7 | 0 | 1 | 1 | 0 | 0 | 0 | y(未注册) |
| 8 | 0 | 1 | 1 | 1 | 0 | 0 | y(未注册) |
| 9 | 1 | 0 | 0 | 0 | 0 | 0 | z(积分不足) |
| 10 | 1 | 0 | 0 | 1 | 0 | 0 | z(积分不足) |
| 11 | 1 | 0 | 1 | 0 | 0 | 0 | z(积分不足) |
| 12 | 1 | 0 | 1 | 1 | 0 | 0 | z(积分不足) |
| 13 | 1 | 1 | 0 | 0 | 1 | 0 | w(库存不足) |
| 14 | 1 | 1 | 0 | 1 | 1 | 0 | w(库存不足) |
| 15 | 1 | 1 | 1 | 0 | 1 | 0 | v(有欠款) |
| 16 | 1 | 1 | 1 | 1 | 1 | 1 | x(兑换成功) |
这张表就是标准判定表的雏形。你可能会觉得16行里有一大半看着重复,比如序号1到8全是“未注册”,那它们能不能合并?能,但不是无脑合并。合并的原则是:如果某些原因在当前分支下对结果完全无影响,那就可以把多个列合并成一列,同时把无影响的条件位写成“-”(不关心)。
比如序号1到8,只要a=0,无论b、c、d是什么,结果都是y,所以这8行可以合并成一行:a=0,b、c、d=-,结果为y。同理,序号9到12合并成a=1、b=0、c、d=-,结果为z。序号13和14合并成a=1、b=1、c=0、d=-,结果为w。剩下两行各成一列。化简后的判定表只有5列:
| 原因\条件组合 | 组合1 | 组合2 | 组合3 | 组合4 | 组合5 |
|---|---|---|---|---|---|
| a(注册会员) | 0 | 1 | 1 | 1 | 1 |
| b(积分≥1000) | - | 0 | 1 | 1 | 1 |
| c(库存充足) | - | - | 0 | 1 | 1 |
| d(无欠款) | - | - | - | 0 | 1 |
| 结果 | y | z | w | v | x |
化简之后,判定表从16列收敛到5列,但每列代表的是一整类等价组合,覆盖度没有减少。我自己画判定表时,通常先列全量组合,再在表上做化简,而不是一开始凭感觉只写几个分支,因为全量组合表能当“查漏清单”用,防止我漏掉某个边界方向。
4. 从判定表落到测试用例:覆盖度与可执行性
4.1 判定表到测试用例的映射
判定表里的每一列,拆成测试用例很直接:一列对应一组原因取值,把这个取值翻译成真实的测试数据,预期结果是那一列对应的输出。按上表的5列,我至少会生成下面这几条用例:
| 用例编号 | 数据准备 | 预期结果 |
|---|---|---|
| TC01 | 用户未注册,其余数据随便造 | 提示“请先注册会员” |
| TC02 | 注册用户,积分999,库存充足,无欠款 | 提示“积分不足” |
| TC03 | 注册用户,积分1000,库存为0,无欠款 | 提示“商品库存不足” |
| TC04 | 注册用户,积分1000,库存充足,有一条未结清订单 | 提示“存在未结清欠款” |
| TC05 | 注册用户,积分1000,库存充足,无欠款 | 兑换成功,积分扣减1000 |
但这里我必须强调,判定表给了组合骨架,不等于测试用例可以完全照搬。至少有三个地方要二次加工。
第一个是边界值要叠加上去。“积分≥1000”这个条件,光测999太粗糙,至少要补上“积分=1000”“积分=999”“积分=1001”三档。库存同理,要补“库存=0”“库存=1”。欠款这种布尔量虽然没有数值边界,但是要确认“欠款金额为0”和“有一笔金额极小的欠款(比如0.01元)”是不是有不同的处理逻辑。别小看这种极小的欠款,有的系统用float判断时,0.01元欠款还真可能因为精度问题被漏判。
第二个是异常分支和系统约束的叠加。如果一个页面允许用户在未登录状态下发起兑换请求,你要在TC01之外再补一个“未登录用户直接被拦截到登录页”的用例。如果系统切换了维护模式,还要补一条“正常兑换路径在高可用开关关闭时被屏蔽”的用例。这些在判定表里没单独体现,但是真实的业务约束。
第三个是UI层和接口层的差异。同一个判定表列,在UI层可能因为按钮置灰导致某些分支进入不了,但在接口层却能直接透传。比如“积分不足”时前端把兑换按钮置灰,用户根本点不到,可是接口没做校验的话,用抓包工具直接调接口照样能提交。所以结算测试用例的时候,我一般把判定表的列拆成“前端用例”和“接口用例”两套来写,前面列举的是接口层面的行为。
4.2 用等价类思想给判定表“瘦身”
刚才的例子只有4个原因,全组合16种,还不算太夸张。真实项目里一个功能4个原因算少的,5个、6个很常见,2的6次方就是64列,全列出来工作量不小。这时候不能硬刚全组合,得学会揉等价类和边界值。
我常用的做法是:对于每一个布尔原因,先确认它的“假值”分支里,有哪些更细分的状态需要单独测试。比如“库存充足”这个布尔量,库存=0和库存=1虽然是同一个布尔分支,但边界值逻辑明显不同,所以至少拆成两条用例。而“注册会员”这个原因,它的“非”分支——未注册和已禁用,在业务上提示文案可能不同,那就得拆成两条。核心思路是:判定表负责组合覆盖,等价类和边界值负责单条件的取值细节,两者叠着用,不要互相替代。
网上也有工具能根据判定表自动生成最小测试用例集,像一些商业测试管理平台、开源的Cucumber结合业务规则引擎,都能干这个事。但我的经验是:工具能帮你跑数,不能帮你判断哪些组合是业务上无意义的。比如“未注册用户且积分≥1000”,工具会老老实实列出来,但你需要判断这个组合在系统里能不能真实存在,不能的话就别拿它去造数据,否则测出来的“提示注册”结果参考价值不大。
4.3 测试数据准备与结果断言细节
按照判定表写用例时,最难的不是画表格,而是造数据。尤其像这个积分兑换案例,涉及到账户余额、积分、欠款状态、库存等多张表的数据联动,造不好会出现“用例之间互相污染”的情况。
我的建议是:每个用例尽量独立,在用例前置里写清楚要预置哪些数据,比如“创建一个注册用户,积分1000积分,账户无未结清订单,给指定商品设置库存为0”。具体到自动化测试里,这些前置可以用接口造数,或者用数据库直接插入测试数据。但要注意,积分扣减是个比较敏感的状态变更,如果TC05跑完之后用户积分变成0,那重复执行时积分不足,用例会失败,所以最好在断言之外加上清理逻辑,跑完用例把数据还原。
另一个细节是断言别只断“页面提示了文案”,要断“状态真的变了”。比如TC05的断言至少包含三层:页面提示兑换成功、数据库里用户积分扣减了1000、兑换订单的状态变为已完成。只断第一层,一旦后端扣减逻辑出错,测试照样绿得发光,等你发现的时候就晚了。
5. 因果图分析法的“坑”与团队落地建议
5.1 最容易踩的三个坑:过度建模、约束乱用、判定表膨胀
我先说第一个坑,过度建模。有的人一旦学会因果图,恨不得所有需求都画一张,连“用户名不为空”这种单条件逻辑都要塞进因果图里,结果就是图越来越复杂,评审时没人看得下去。因果图的最优场景是3到6个原因、有明确逻辑依赖的模块,太简单的逻辑直接写两条用例就够了,太复杂的逻辑也不是一张图能解决的。
第二个坑是约束符号乱用。E、I、O、R、M五个符号,含义和场景不同,混用会直接影响判定表生成。比如有人把“支付方式二选一”画成R约束,但R的意思是“一个成立时另一个必须成立”,这明显不对,应该是E约束。画错一个约束,后面整个判定表的组合排除就全错了,所以画图时我会把一个“约束符号对照表”放在手边,每画一个约束先默念一遍它的定义。
第三个坑是判定表膨胀之后失去可执行性。原因数量一多,全组合数量指数级上升,如果你真的老老实实把所有组合都转成测试用例,用例库会瞬间爆炸,开发和测试都扛不住。这时候应该回到第4部分说的“化简+叠加边界值”的思路,用“有效组合”而不是“全组合”来驱动用例设计。我见过不少团队用因果图分析出一个四五十条的判定表,然后老板问“这些用例必须全跑吗?”——这就是没做好约束和化简。
5.2 图规模失控时的替代方案
当原因数量超过6个,我的处理方式基本是:因果图只用来“讲逻辑”,不用来“枚举组合”。什么意思?我仍然会画图,目的是在需求评审阶段拉着产品、开发、测试一起对标逻辑关系,把“或”和“与”的歧义消除掉。但到用例设计阶段,我改用Pairwise法(成对组合测试)来压缩组合空间。
Pairwise的思路是:大部分缺陷都是由“单个条件”或“任意两个条件组合”触发的,三个以上条件同时触发缺陷的概率明显降低。所以Pairwise只保证任意两个条件的取值组合都至少覆盖一遍,这样能把组合数从十几次压缩到六七次,代价是有可能漏掉高阶组合缺陷。所以我在用Pairwise之前,一定会先用因果图把所有约束标出来,再用约束去过滤Pairwise生成的组合,保证过滤后的组合仍然是业务可执行的。
还有一个实用技巧是把一个“大功能”拆成“多个小判定表”。比如积分兑换、库存扣减、积分流水记录这三个子功能,各画各的因果图、各出各的判定表,最后用“串行场景”把它们串起来验证整体链路。比起画一张巨型因果图试图覆盖全部逻辑,拆开做更可控,排错也更快。
5.3 因果图分析法和测试分层策略怎么结合
因果图分析出来的用例,我更倾向于用在“接口层”和“单元集成层”,而不是拿它硬套UI层。原因很简单,UI层的很多分支被前端拦截器挡住了,比如按钮置灰、下拉框不可选,这些交互逻辑本质上是另一套规则。如果你把因果图表上的所有组合都放到UI层去点,你会发现有些组合根本点不出来,会误判成“用例执行不通过”。
接口层就不一样,接口层可以直接传任意参数组合,正好符合因果图枚举组合的特点。所以我的落地实践是:先把判定表翻译出的核心组合用例放在接口自动化里做回归,再把UI层单独的交互分支(比如按钮置灰、弹窗提示)拿出来做少量UI用例。这样既保证了组合覆盖,又不会因为UI层太多无效操作拖慢执行效率。
5.4 给团队的落地建议
因果图法在团队里推广,最大的阻力不是方法本身复杂,而是大家觉得“画图太费时间”。我的破局办法是先在项目里找到一个真出过故障的模块做试点,把需求原文贴出来,画一张因果图,再出几张对应的测试用例,拿“如果没有因果图,哪几条用例会被漏掉”来说事。一旦团队直观感受到这个方法能补上之前的测试盲区,后续推广就顺了。
另外,我强烈建议在需求评审阶段就用因果图来薅需求问题。因果图要求把所有原因和结果都显式列出来,这本身就是一次需求澄清。评审时拿着因果图问产品经理:“如果用户未注册但积分够,你希望他看到什么提示?”——很多时候产品经理也要愣一下才能回答。能在评审阶段逼出这种问题,就已经值回画图的时间了。
我在实际项目里用因果图分析法的体会是,它真正牛的地方不是帮你把测试用例写得多漂亮,而是逼着你把需求逻辑彻底想清楚。很多隐含规则、二义性表述、条件组合漏洞,都在画图的过程中被翻了出来。如果你也被“条件组合漏测”坑过,不妨下次遇到多头绪的业务规则时,拿张纸出来画一画因果图,再落成判定表,你会和我一样,少接到几个深夜的线上告警电话。