☰
因果图分析法:从逻辑建模到判定表的测试用例设计实战
2026/10/1 4:52:48 网站建设 项目流程

做了多年的软件测试和需求分析,我越来越觉得,测试用例设计这门手艺,真正拉开差距的往往不是等价类、边界值这些基本功,而是面对一堆纠缠不清的输入条件时,你能不能把逻辑理顺、把组合覆盖到位。因果图分析法,就是我在这种情况下最常用、也最顺手的一件武器。它不玄乎,本质上是把需求里“什么条件组合导致什么结果”的逻辑,先画成一张图,再转成一张表,最后变成可执行的测试用例。这篇东西,我把从画图到转判定表、再到生成用例的完整思路和踩坑经验都整理出来,希望能给正在为复杂条件组合头疼的同行一些参考。

1. 因果图分析法的核心逻辑与适用场景

1.1 为什么等价类和边界值搞不定时,就该轮到因果图

先说说这个方法的定位。等价类划分和边界值分析,擅长处理的是单个输入条件、或者彼此独立的多个输入条件。比如一个手机号输入框,有效是11位数字,无效是小于11位、大于11位、含非数字,这些情况每个条件都能独立设计用例,互不干扰。可一旦遇到这样的需求:用户输入用户名和密码,只有当两者都正确、且验证码也正确时,系统才允许登录,任意一项错误都给出对应提示,此时条件之间就开始有“逻辑纠缠”了。

这类需求的麻烦在于,测试人员面对的不是“每个输入是否有效”,而是“多个输入条件的组合如何决定最终输出”。更复杂的场景里还夹杂着优先级:比如用户名错误和密码错误同时发生时,系统只提示“用户名不存在”;再比如某些条件下某些字段不可用、某些策略不生效。这些逻辑如果靠拍脑袋点几个组合去测,很容易漏掉某个组合分支,尤其当条件达到四五个以上时,凭直觉枚举组合基本不可能完整。

因果图分析法解决的就是这个问题。它把需求描述的因果关系当作一张逻辑网络来处理:人为确定的“原因”是输入条件,“结果”是系统响应,用标准化的图形符号把两者之间的与、或、非以及各种约束关系画出来,再把这张图转化为判定表,最终设计出覆盖所有有效逻辑组合的测试用例。它不是替代等价类和边界值,而是接手它们处理不了的那部分——多条件组合逻辑。

1.2 什么样的需求最适合用因果图,什么样的不适合

从实际项目里摸出来的经验,因果图分析法在下面这几类场景效果特别好:

  • 条件之间存在明显的与、或、非关系,组合结果多样。
  • 多个输入条件之间存在约束,比如互斥(选A不能再选B)、要求(选了C必须先选D)、屏蔽(出现E则F不生效)。
  • 错误提示的优先级依赖条件组合,同一类异常在不同组合下响应不一样。
  • 业务规则多,比如优惠券能否叠加、订单状态能否流转、审批节点通过或驳回的分支条件、支付渠道的可用性判断。

反过来,如果需求本身没有逻辑组合,只是一堆数值计算或者简单线性流程,因果图就属于杀鸡用牛刀。举个典型的例子,计算订单总价的规则是“商品单价乘以数量,加上运费,减去折扣”,这个用等价类和边界值就能搞得明明白白,强行画因果图反而浪费时间。因果图的成本在于建模和转表本身有一定工作量,条件少于三个、结果单一的场景,收益不大。

1.3 因果图和判定表是前后脚的关系,不是二选一

不少文章把因果图和判定表分开讲,好像它们是两种并列的方法。我实际用下来,它们更像一套流水线的两个工序。因果图的价值在“梳理”——把让人头晕的文字需求变成一张一目了然的逻辑图;判定表的价值在“落地”——把图里的逻辑关系展开成所有可能组合的二维表,每一列就是一条规则,可以直接对应成测试用例。

画因果图的过程,本身就是在帮测试人员强制建立全局逻辑观。很多时候我画着画着,就会发现需求里的矛盾点:比如某个结果在约束条件下根本不可能达成,或者某个因被遗忘了,再或者某两个条件放在一起永远为假,根本测不了。这些用文字通读需求时很难一眼看穿,一旦落到图形化、结构化的表达上,逻辑漏洞自己就会冒出来。这也是为什么我一直建议团队做复杂需求时,先画因果图,再转判定表,这个顺序不要反过来。

2. 因果图设计的完整流程与实操步骤

2.1 从需求文本中提取原因与结果,这一步决定了成败

画因果图的第一步,不是急着连线,而是先把需求里的“原因”和“结果”都摘出来。我的习惯是把需求文档打开,从头到尾逐句读,凡是看到“如果……那么……”“当……时……”“……才……”这类描述,都高亮出来。原因通常是输入条件、中间状态、触发动作;结果则是系统产生的响应、输出、提示或状态变更。

打个比方,把需求想象成一个电路,原因是开关,结果是灯泡。你首先要做的,是数清楚有哪些开关、哪些灯泡,然后才谈得上连接它们。这里有个很实用的小技巧:把原因和结果分别用符号标注,我习惯用Ci表示原因(Cause),Ei表示结果(Effect),在纸上列成两组清单。比如“用户名错误”“密码错误”“验证码错误”是原因,“提示用户名不存在”“提示密码错误”“允许登录”是结果。列完之后反复比对需求,看有没有遗漏的状态。

这一步最容易犯的错误是粒度不统一。有人把“输入正确用户名”和“输入正确密码”合并成一个原因“输入正确”,导致因果图完全无法表达两个错误同时发生时的优先级逻辑;有人又把一个原因的多个细微状态拆得过细,比如“密码包含大写字母”“密码包含数字”,但这些并不是独立的业务决策条件,拆了只会让图爆炸。判断标准很简单:在需求逻辑中,这个条件是否会被单独判断、单独引发不同结果?会——就拆出来;不会——就归并或降到数据层面用等价类处理。

2.2 因果图的基本符号与约束关系,画图前必须掌握的语法

因果图有一套标准的符号体系,不复杂,但每个符号对应一种逻辑关系,画之前最好先达成共识。我平时用的最核心的四种关系符号:

  • 恒等。原因出现,结果必出现;原因不出现,结果也不出现。C1与E1直接连接,代表一一对应的逻辑。
  • 非。原因出现,结果不出现;原因不出现,结果出现,也就是取反。
  • 或。几个原因中至少有一个出现,结果就出现;全部不出现,结果才不出现。
  • 与。几个原因必须全部出现,结果才出现;任意一个不出现,结果就不出现。

除了这四种基本关系,因果图里还会用约束条件来表达原因与原因之间、结果与结果之间的限制关系。这些约束在需求里经常被一句带过,但恰恰是测试用例最容易覆盖不到的地方。常用的约束符号包括以下几种:

  • E(互斥)。两个原因不会同时成立。比如订单支付方式选“余额支付”和“在线支付”在同一个支付动作中互斥。
  • I(包含)。多个原因中至少有一个成立。比如“投诉方式”可以通过电话、邮件、在线客服三种渠道,至少要选一个。
  • O(唯一)。多个原因中有且只有一个成立。比如订单状态是新建、已支付、已发货、已完成中的唯一一个。
  • R(要求)。一个原因出现时,另一个原因必须也出现。比如勾选了“同意用户协议”,要求“必须勾选”这个原因成立,否则不能提交。
  • M(屏蔽)。一个原因出现时,另一个原因就不会出现。比如用户输入了“新密码”,旧的“确认密码”输入就被屏蔽或忽略。

我把这些约束关系的位置放在原因层,因为它们描述的是输入条件彼此间的关系。实际项目中,互斥和要求的出现频率最高,如果不能准确识别,判定表里就会出现无效列。

2.3 画图的操作流程与检查要点,如何保证因果图不跑偏

画因果图的实操步骤,我建议按下面的顺序来:

  1. 把提取出的所有原因放在图左侧,所有结果放在图右侧。
  2. 先连接直接、明显的因果关系,特别是那些一一对应、直接导致提示或结果的关系。
  3. 再识别组合关系:回头看哪些结果不是单个原因决定,而是多个原因组合后才触发的,补上与、或逻辑节点。
  4. 接着标记约束:原因之间存在互斥、包含、唯一、要求、屏蔽等关系时,在原因之间用约束符号标注。
  5. 最后走查一遍:把图上的原因逐个假设为“出现/不出现”,推演结果,看是否与需求一致。

走查这一步非常关键,相当于对需求做了一次逻辑测试。实际工作中我有过这样的经历:某个需求描述“用户申请退款时,如果订单已发货,则退款必须经人工审核”,但同一句话里又说“已发货订单不能申请退款”。这两个描述在因果图上同时画出来,就是典型的“原因已发货”与结果“允许申请退款”之间出现矛盾。画完图走查时发现这个冲突,再去问产品经理,对方才承认需求文档这里写错了。这种问题,靠通读文档写用例根本不容易发现。

还有一点要提醒:因果图不是越复杂越好。如果一个图上的原因超过六七个、连线密密麻麻,先别急着硬画,停下来考虑是不是需求模块太大,应该拆成几张图分别处理。因果图的优势在于清晰表达逻辑,一旦画到连自己都看不清,就失去了意义。

3. 从因果图到测试用例:判定表转换与用例生成

3.1 为什么因果图必须转成判定表,而不是直接写用例

有段时间我刚接触因果图,画完图就迫不及待地开始写用例,写了两三条之后发现不对劲:因果图表达的是逻辑结构,却没有直接给出“有哪些条件组合需要测试”的清单。比如三个原因分别与一个结果有与关系,理论上就有2的3次方等于8种条件组合,哪些组合是有效的、哪些根本不可能发生,因果图只画到了关系层,没有展开到组合层。

判定表就是用来做这件事的。它把每个原因作为一列条件桩,每个条件项的取值设为0或1(代表不出现/出现),把每个结果作为一列动作桩,每一条规则对应一行条件组合和在此组合下的动作取值。判定表天然覆盖了所有逻辑可能,并且在化简规则时能挤掉大量重复或矛盾组合。

在我看来,因果图负责“画逻辑”,判定表负责“算组合”,少了哪一步都不完整。直接用因果图设计用例,往往是凭直觉挑几条组合,等于又走回了拍脑袋的老路;而直接跳过因果图去写判定表,面对复杂需求时又容易理不清逻辑关系,画错边的概率大增。先图后表,是最稳的路径。

3.2 完整案例:一个登录验证模块的因果图测试设计实操

我拿一个经典但又够典型的登录验证需求来完整走一遍流程。需求描述是这样的:

  • 用户名、密码、短信验证码均为必填项。
  • 当用户名错误时,无论密码和验证码是否正确,系统都只提示“用户名不存在”。
  • 当用户名正确、密码错误时,无论验证码是否正确,系统都只提示“密码错误”。
  • 当用户名和密码均正确、验证码错误时,系统提示“验证码错误”。
  • 当用户名、密码、验证码全部正确时,允许登录。

先列原因:

  • C1:用户名为正确的已注册账号
  • C2:密码正确
  • C3:验证码正确

再列结果:

  • E1:提示“用户名不存在”
  • E2:提示“密码错误”
  • E3:提示“验证码错误”
  • E4:登录成功

这个需求有一个隐藏的优先级:用户名错误时,后面所有判断都不再看;用户名正确但密码错误时,验证码判断被跳过。这在因果图上表现为一种“屏蔽”效果,本质上就是约束关系中的M(屏蔽)。

根据逻辑关系画因果图:

  • C1错误则触发E1。
  • C1正确、C2错误,触发E2;且此时C3结果被屏蔽,不参与判断。
  • C1、C2正确、C3错误,触发E3。
  • C1、C2、C3均正确,触发E4。

转成判定表时,三个条件各取0或1,理论上共有8条规则。但其中有几个组合因为总逻辑的存在根本不会出现或不会生效,比如C1为0(用户名错误)时,C2、C3无论取什么值,结果都只有E1。合并后得到的规则如下(规则列只列有效逻辑分支):

规则号C1用户名正确C2密码正确C3验证码正确E1提示用户名不存在E2提示密码错误E3提示验证码错误E4登录成功
10--1000
210-0100
31100010
41110001

这里“-”代表该条件下此因素不影响结果,也就是我们在判定表里做规则合并时把无关条件项置为不关心符。这4条规则,每条规则对应一条测试用例,再用边界值给每个输入条件补上非法数据细节(比如用户名不存在但有非法字符、验证码输错一位等),最终就能形成一份不错的测试用例集。

可能有人会问:C1为0、C2为0时,真实系统到底是提示“用户名不存在”还是“密码错误”?判定的依据是需求中的优先级描述。大多数登录系统的设计是先查用户是否存在,用户不存在时根本不会去校验密码,所以此时无论密码对不对,提示都应该只有“用户名不存在”。但这也是需要和产品确认确认的点,测试用例里应明确定义预期结果。

3.3 用例覆盖率的取舍:如何用最小成本覆盖高价值逻辑

设计测试用例时难免会焦虑:判定表展开后条件组合太多,全测工作量大;条件组合太少,又怕漏测关键分支。根据经验,我一般按三个层次来做取舍。

第一层,保证每个结果至少被触发一次。像上面案例,4种结果都被覆盖到,这是底线。第二层,保证每个原因的有效和无效状态都至少参与过一次。比如C1覆盖了正确和错误两种状态,C2、C3也类似。第三层,关注条件组合的边界与异常叠加。这里的“边界”不是数值上的边界,而是逻辑上的边界,比如“用户名正确、密码正确、验证码正确”这种全通过路径,“用户名错误但密码和验证码都正确”这种大量条件同时成立的错误路径。

如果条件继续增多,判定表的规则数会以2的n次方速度膨胀。这时候不要贪心去测完所有组合,而是先结合业务风险给条件分权重。核心业务逻辑、资金相关、权限相关的路径优先全覆盖;次要路径可以结合正交实验法,用成对组合来压缩用例规模。因果图加判定表的组合拳,在“保证不遗漏关键组合”和“控制用例数量爆炸”这两个目标之间,能做到一个很好的平衡。

4. 常见问题与排查技巧实录

4.1 因果图画到一半,发现需求本身自相矛盾怎么办

这个我在前文提过一嘴,但值得单独拿出来细说,因为它是因果图实践中最常见、也最有价值的“副作用”。某个电商后台的需求写:当订单状态为“已发货”时,用户可以申请退款;另一个地方又写:申请退款时校验订单状态,若为“已发货”则提示不能申请。两个描述放在因果图上,原因C1“订单已发货”与结果E1“允许退款”的关系,一张图里又画恒等又画非,直接冲突。

遇到这种情况,千万不要自己拍板选一个解释去画图。正确做法是把矛盾点标记出来,整理成问题清单,发给产品经理或需求方确认。很多时候这类矛盾是需求文本在不同章节由不同人撰写造成的,确认后往往会改需求或者判定测试的预期结果,这些都必须有明确的结论。因果图在这里的真正作用,是成为和产品沟通的公共语言,双方看着图讨论,比对着文字掰扯效率高得多。

4.2 条件太多导致组合爆炸,怎么拆解才高效

当原因数量超过六七个、且彼此之间约束少的时候,判定表规则数会迅速膨胀到几十上百条。这时候硬啃因果图效率很低,我的处理方式是先拆模块、再分层、后过滤。

先说拆模块。如果一个支付流程同时涉及支付方式、商品类型、用户等级、优惠券、库存状态等多个维度,先不要试图画一张大而全的因果图,而是按业务阶段拆:创建订单阶段的规则、支付阶段的规则、支付成功回调阶段的规则,分别画图、分别转表。阶段之间只保留必要的状态传递,不把跨阶段的条件全部混在一张图里。

再说分层。区分哪些条件是决定功能流程是否走下去的关键条件,哪些条件只是影响某个字段显示或某个参数取值。关键条件决定主干逻辑,必须进因果图;字段显示类的,可以用等价类加界面校验表来处理。

最后说过滤。判定表转出来以后,逐列检查有没有违反约束的无效组合,比如E约束下两个原因同时为1、O约束下允许了多个1的列,这些列可以直接删掉。综合用这几个办法,绝大多数项目的用例数量能控制在可接受范围。

4.3 从因果图转判定表时,最容易踩的几个坑

这个环节我踩过的坑不少,挑三个最典型的说说。

第一个坑:把“与”关系误划成“或”关系,或者反过来。原因一般在图形上看起来差不多,但转换到判定表,与关系的规则行只有一个组合是1,或关系则有多行是1,一旦这里画错,结果预测全错。后来我习惯在画完图后,把每个结果对应的原因关系用逻辑表达式写一遍,比如E4 = C1 AND C2 AND C3,作为转表的对照基准。

第二个坑:因果图上的约束关系没有落到判定表里。比如两个原因之间画了E约束,但转判定表时还是把所有组合都列出来,表格里会出现“用户名正确且用户名错误”同时为1这种物理上不可能的规则。约束关系就是判定表的过滤条件,转表之后必须做一遍约束校验。

第三个坑:规则合并时把不同动作的规则强行合到一列。规则合并只能针对结果完全相同、条件中存在无关项的规则,如果结果不同,即便条件项再接近也不能合并。比如“用户名错误”和“密码错误”如果提示信息不同,即使某个组合下条件项只有一个不同,也绝不能合并成一条规则,否则输出动作对不上。

4.4 画图工具和辅助手段,怎么选更顺手

工具层面我没什么执念,用过Visio、ProcessOn、draw.io,也直接在纸上画过。线上协作项目比较多时,我习惯用ProcessOn或draw.io,好处是团队成员能直接看图提意见,评审时不用把图截来截去。如果公司有内网的文档系统,很多也自带画图能力,直接用就行。

真正提升效率的辅助手段是,用Excel或在线表格把判定表的生成半自动化。原因数量多的时候,手工展开所有条件组合很容易漏。我个人的做法是先在表格里建一个二进制组合生成区:把n个条件的全部取值组合用数学上的二进制展开方式列出来,然后结合因果图上的逻辑关系写对应公式,自动算出每个结果的动作值。比如C1对应的单元格填IF公式判断输入值,E1对应的单元格参照逻辑表达式返回0或1。这样能快速生成完整判定表,再手动删掉无效组合,比纯手工快很多。写脚本也行,但大多数项目用Excel公式足够,没必要为了画因果图专门写程序。

写在最后的实操心得

关于因果图分析法,我个人的体会是,它看着像一种画图技巧,本质上是一种强制性的逻辑训练。每次画完一张因果图,我都能对这个业务模块的逻辑关系有更深的理解,这是单纯照需求写用例给不了的。尤其是那种规则相互嵌套、异常分支极多的模块,因果图法几乎就是测试用例设计的定海神针。

最后再分享一个小技巧:在实际项目中,不要等到测试设计阶段才开始画因果图。需求评审阶段,只要看到复杂的条件组合描述,就当场在笔记本上画一个简化版的因果图草稿,往往能立刻发现问题,把矛盾消灭在评审环节。这比等到用例设计阶段再发现需求问题,要省太多事了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询