☰
因果图与判定表:从需求逻辑到测试用例的完整实践
2026/10/1 4:54:19 网站建设 项目流程

测试方法论里一直有个老生常谈但又特别容易被忽视的工具:因果图。讲真,我见过很多测试同学能把等价类、边界值说得头头是道,一上手全用等价类划分硬怼,结果需求里那些条件组合关系、互斥约束经常被漏掉,最后线上出了bug才追悔莫及。因果图这个方法的定位恰恰就是补这块短板的——它专门处理“多个输入条件之间有逻辑关系、有组合约束”的场景,帮你在设计用例之前先把逻辑理清楚,把该覆盖的组合找全。

这篇东西我准备从原理讲到落地,用实际的案例把画图、转判定表、生成用例的完整链路走一遍,最后再聊聊我踩过的坑和总结的经验。适合刚入行想系统学测试设计的同学,也适合做了一段时间但用例设计基本靠“拍脑袋”的同行参考。

1. 因果图的本质:不是画图,是把需求的逻辑结构显性化

很多人一听“因果图”就觉得是要画一张花里胡哨的图,甚至因为嫌画图麻烦就直接跳过这个步骤。这是对因果图最大的误解。因果图的重点从来不是那张图本身,而是它强迫你从需求文本里提炼出“原因”和“结果”,然后明确它们之间的逻辑关系。一旦这个工作做透了,后面测试用例的生成几乎是水到渠成的事。

拿一个最常见的登录功能来举例。需求里可能写着“用户名存在且密码正确时,允许登录;用户名存在但密码错误时,提示密码错误;用户名不存在时,提示用户不存在”。如果你用等价类划分,可能就会列出几个有效等价类和无效等价类,分别设计几条用例。但这里有个细节——用户名存在和密码正确这两个条件之间其实是“并且”的关系,而且用户名不存在时还需要考虑密码到底填没填,这又牵扯出条件之间的约束。这种组合关系的梳理,正是因果图的用武之地。

从方法论的角度看,因果图属于典型的黑盒测试设计方法。它不关心代码内部怎么实现,只看外部输入和输出之间的逻辑映射。这个方法的核心价值有几个:

  • 把自然语言描述的需求转换成结构化的逻辑模型,减少需求理解偏差;
  • 识别出多个条件之间的依赖与互斥关系,避免组合场景漏测;
  • 为生成判定表提供输入,让用例设计从“凭感觉枚举”变为“按规则穷举”。

如果你把因果图当成一个思考工具,而不是一个画图任务,你就能明白它的威力所在。我在实际项目中用过很多次,尤其是接手那些逻辑复杂的老系统时,因果图几乎是我梳理需求的第一道工序。它能在需求评审阶段就帮你发现矛盾点和遗漏点,比写用例时再来返工效率高得多。

2. 因果图的核心要素:原因、结果、中间节点和约束关系

要用好因果图,先得把这些基本符号和概念吃透。这不是为了考试,而是为了在实战中快速上手。

2.1 原因、结果与中间节点

因果图里的“原因”指输入条件,“结果”指输出或系统动作。比如下单时“库存充足”是一个原因,“订单创建成功”是结果。有些场景下逻辑链路比较长,原因和结果之间有多个层次的中间判断,这时就需要引入中间节点。中间节点不是必须的,但它能让复杂的逻辑图变得更易读,不至于一条线拉到底、乱成一团。

举个实际例子:一个营销活动系统,规则是“用户为会员且购物车金额满200元,或非会员但购物车金额满500元时,可以使用优惠券”。这里就有四个原始原因:

  • 用户是会员
  • 购物车金额满200元
  • 用户不是会员(注意,这个其实是“用户是会员”的反面,实际建模时要么建两个原因,要么用“非”逻辑处理)
  • 购物车金额满500元

输出结果是“可以使用优惠券”。但认真拆一下,“会员且金额满200”是一个中间判断,“非会员且金额满500”是另一个中间判断,这两个判断再组合成最终输出。如果你不画中间节点,直接四条线连到结果上表达这个逻辑,图会非常绕。有了中间节点,图的层次感就出来了。

2.2 四种基本逻辑关系

因果图里最常用的逻辑关系就四种,必须烂熟于心:

  • 恒等:原因成立,结果就成立;原因不成立,结果也不成立。这个最简单,但别忽略它——很多系统里“直接透传”的逻辑就属于这种。
  • 非:原因成立时结果不成立,原因不成立时结果反而成立。经典的例子是“账号被锁定”时“允许登录”这个结果不成立。
  • 或:几个原因中任意一个成立,结果就成立。比如“密码错误次数超限或账号被管理员冻结”时“禁止登录”。
  • 与:所有原因都成立,结果才成立。比如“用户名存在”且“密码正确”才允许登录。

这些逻辑关系本质上就是布尔运算的与或非。画因果图时用不同符号把原因连接成中间判断或者最终结果,整个需求逻辑就一目了然了。我自己在实际工作中,经常画着画着就会发现需求文档里逻辑没写清楚的地方,比如某个条件的优先级没说、两个条件互斥但规则没定义,这些都是画图的额外收获。

2.3 约束关系:不是所有组合都合法

因果图里还有一个特别重要的部分——约束。它描述的是输入条件之间或输出结果之间的“不可能组合”。这些约束是从业务语义或者系统规则里来的,不画清楚的话,后面生成判定表时会列出很多业务上根本不存在的组合,白白增加无用用例。

常用的约束符号有这几类:

约束类型针对对象含义例子
E(异)原因最多一个原因成立“手机号登录”和“邮箱登录”在同一登录框里最多二选一
I(或)原因至少一个原因成立查询条件“姓名”和“工号”至少要填一个
O(唯一)原因有且只有一个成立“性别男”、“性别女”二选一
R(要求)原因一个原因成立时另一个必须成立“选择了国际航班”要求“填写护照号”
M(强制)结果一个结果成立时另一个结果必须不成立“登录成功”和“登录失败”互斥

这几种约束关系在实际业务中出现的频率非常高。比如一个下拉菜单选择“其他”时必须填写备注字段,这就是典型的R约束;一个订单状态不能同时是“待支付”和“已支付”,这是结果层面的M约束。因果图里把这些约束标出来,是为了后续用例设计时不去生成那些非法组合,同时又不能漏掉对非法组合本身的测试。

3. 因果图实操全流程:从需求到用例的一次完整走查

这一节我用一个稍微复杂一点的案例——电商优惠券使用规则,把因果图从需求解读到最终生成测试用例的整个流程完整走一遍。这个案例糅合了我实际工作里遇到的不少细节问题,很有代表性。

3.1 案例需求描述

假设有一个电商系统,优惠券使用的校验规则如下:

  • 用户必须登录;
  • 优惠券必须是未使用状态;
  • 优惠券必须属于当前用户;
  • 订单金额必须达到优惠券的最低使用门槛;
  • 如果以上条件都满足,则可以使用优惠券,系统计算优惠金额;如果未登录或优惠券不属于当前用户,则提示“无权使用该优惠券”;如果优惠券已使用或订单金额不足门槛,则提示“优惠券不可用”。

这段需求看起来不算难,但仔细一抠,里面藏着不少逻辑问题。比如“未登录”和“优惠券不属于当前用户”同时发生时,提示语应该显示哪一条?需求里没有定义。再比如订单金额不足和优惠券已使用同时发生时,提示优先级是什么?这些都是在画因果图过程中需要和产品确认的细节。如果直接上手写用例,大概率会漏掉这些边界情况的校验。

3.2 第一步:提取原因和结果

我先明确原因(输入条件):

  • C1:用户已登录
  • C2:优惠券属于当前用户
  • C3:优惠券未使用
  • C4:订单金额达到使用门槛

再看结果(输出动作):

  • E1:使用成功,计算优惠金额
  • E2:提示“无权使用该优惠券”
  • E3:提示“优惠券不可用”

这里可能有人会问,“未登录”和“优惠券不属于当前用户”明明是两个原因,它们都能导向同一个结果E2,那E2是不是应该分解成两条不同的提示?从用户视角看,提示语一样,但从系统实现看,校验的路径完全不一样。我在实际项目中通常会把结果细化到“可观察行为的级别”,提示语相同但触发条件不同时,还是算同一个结果,但在判定表备注里注明不同的原因组合。这样既不会过度拆解导致用例爆炸,又能追溯每一个组合的覆盖情况。

3.3 第二步:画因果图并标注逻辑关系

接下来就是把这些原因和结果用逻辑关系串起来。从需求描述看,E1成立的条件很清晰:C1、C2、C3、C4四个条件全部满足,也就是“与”的关系。E2成立的条件是“不是C1或不是C2”,也就是“非C1或非C2”。E3成立的条件是“不是C3或不是C4”。这里我用“非”来表示原因的反面。

注意,这里有一个隐含问题:C2“优惠券属于当前用户”这个条件在用户未登录时怎么取值?从业务逻辑上说,未登录状态下根本无法校验“优惠券是否属于当前用户”,此时这个条件应该视为“不判定”。但从因果图建模角度,我们会给C2两个取值——“属于”和“不属于”。未登录时虽然不该校验C2,但由于整体逻辑有“与”的关系,C1不成立时结果E1肯定不成立;而E2只要“非C1成立”就会触发。所以未登录状态下C2取什么值,不影响最终结果。这正是因果图逻辑梳理带来的好处:你可以推导出各种情况下的系统行为,而不是靠猜。

画图时,我习惯用中间节点把“E2的两个触发分支”和“E3的两个触发分支”分别做个聚合,这样图面上更清晰。中间节点不是可选项,在逻辑分支多的时候非常有用。

3.4 第三步:转判定表,处理约束和无效组合

因果图画完之后,下一步就是转成判定表。为什么非要转判定表?因为因果图表达逻辑关系很直观,但它不适合直接生成测试用例。判定表则天生就是“条件组合”的结构,每一列就是一个组合,再配合动作就能直接映射到测试用例上。

先把四个原因C1、C2、C3、C4的取值组合枚举出来,理论上2的4次方等于16种组合。但加了约束条件后:

  • C1和C2之间有一个隐性约束:未登录(非C1)时,“优惠券属于当前用户”其实不应被校验,但为了逻辑推导,它在表中仍然有两种取值。
  • C2和C3之间可能有业务限制:一张优惠券如果根本不属于当前用户,那它已经使用与否其实是一个未知状态。但从测试角度看,这两种组合仍需覆盖,只不过预期结果要根据规则推导出来。

我实际处理时,不会一上来就撒开把所有组合都列了,而是先把约束明确的组合剔掉,再针对剩余组合逐条推导预期结果。这样判定表最后大约会出12到14列有效组合,远少于原来的16列全组合。实际做项目时如果条件个数达到六七个以上,全组合可能直接上百条,这时候就需要引入工具辅助或者用Pairwise算法进行组合缩减。判断到底穷举还是缩减,核心依据是这些条件之间的独立性:独立性强、业务约束少的,可以考虑Pairwise;约束多、业务关键度高的,建议老老实实全量枚举,别省这个时间。

3.5 第四步:生成测试用例

判定表里的每一列代表一个逻辑组合,把它映射成具体的测试数据,就是一条测试用例。比如某一列的条件是“C1已登录、C2属于当前用户、C3已使用、C4金额达标”,那么对应的测试步骤就是“用一个已登录账号和一张已使用的优惠券,下一笔满足门槛的订单”,预期结果是E3“优惠券不可用”。测试数据要尽量贴合真实业务场景,别用那种一眼假的极端值,不然执行时很容易因为环境数据凑不齐而卡住。

写用例的时候,我习惯每个组合都带上“前置条件”和“预期结果”两栏。前置条件写清楚账号状态、优惠券状态、订单金额这些要素;预期结果除了写系统提示语,还要把数据库层面该有的状态变化也写上,比如“优惠券状态从未使用改为已使用”“订单金额被扣减”等。这样用例本身的可执行性会强很多,测试新人拿到手就知道怎么跑,不会跑完不知道看什么。

4. 因果图落地时常见的坑和一些实操心得

方法论讲得再多,最终还是要回到生产环境里检验。我自己的经验是,因果图真正用起来之后,问题往往不是“不会画”,而是“画了但是画得不准”“画了但是没有坚持用下去”。下面整理几个我在实操中遇到的高频问题和对应的处理心得。

4.1 原因和结果的粒度把握不住

这是新手最容易栽的地方。一个需求拿过来,有的人把原因拆得特别细,比如“用户名长度是否合法”“用户名是否包含特殊字符”“用户名是否与数据库中记录匹配”,结果画出来的图一屏放不下;有的人又把原因合并得太粗,比如直接把“登录成功”当成原因,那就根本没意义了。

我自己把握粒度的原则是:原因必须能映射到某个可独立观察或掉输入的字段/状态上。比如“用户已登录”看的是session状态,“订单金额是否满门槛”看的是金额数值。如果一个描述里包含了两个及以上独立条件,那就要拆开。结果则必须保持在系统对外输出的可观察层面,比如“页面提示”“发送短信”“生成订单”。那些内部计算中间量,除非会直接体现为中间节点,否则不要作为结果写进因果图。

4.2 约束关系画漏,导致无效用例堆砌

因果图里把约束画全,直接决定判定表的列数质量。我见过有人画因果图完全不标约束,结果判定表里出现“未登录且优惠券属于当前用户”这种业务上不可能的测试数据,不仅浪费执行时间,还可能因为造不出对应数据让用例直接废弃。约束关系的来源主要是需求文本的隐含语义,需要和产品经理确认。如果需求里没说清楚,千万不要自己想当然定约束,一定要追问。比如“优惠券属于当前用户”和“用户已登录”之间到底允不允许未登录就查询优惠券归属,不同系统的实现逻辑差异很大,必须拉通对齐。

4.3 因果图画起来耗时,怎么提速

很多团队不愿意用因果图,核心原因就是觉得画图慢。我的经验是:并不是所有需求都需要画完整的因果图。当条件个数少于3个且逻辑关系简单时,直接等价类划分就够了;当条件个数超过6个且互相独立时,强推因果图反而会陷入组合爆炸,此时更适合直接用Pairwise思维去选组合。真正需要用因果图的,是条件个数在3到6个之间、条件之间存在明显的逻辑依赖和约束关系的场景。这类需求往往隐藏的bug最多,最值得花时间画因果图。

具体工具上,我试过Visio、draw.io、ProcessOn,也试过直接用Excel画。说实话,因果图的绘制工具对结果影响不大,关键是思路要清晰。我现在更推荐直接用draw.io,免费、支持多人协作,而且它的图形模板里可以自定义逻辑符号,画起来很快。如果你不想用专门的绘图工具,用Excel的单元格和连线也完全够用,重点是那张判定表,图本身只是辅助思考的载体。

4.4 因果图与判定表的联动,必须坚持到最后一步

这是我特别想强调的一点。很多人画完因果图,觉得逻辑理清楚了,就直接跳到用例设计,判定表这个中间产物被省略了。这是非常可惜的。判定表最大的价值在于它强制你以列的方式穷举条件组合,并标注每个组合的预期动作。如果你跳过了判定表,直接在因果图上数出几条分支就写用例,那和直接用等价类划分没有本质区别,该漏的组合照样漏。

我在评审测试用例时,如果发现测试设计文档里有因果图,我会直接要求作者附上对应的判定表。没有判定表的因果图,基本可以断定是“画完就扔”的假把式。真正把因果图用出效果的人,一定是图、表、用例三件套齐整的。这样评审时别人一眼就能看出哪些组合覆盖了,哪些组合没覆盖,逻辑对不对一目了然。

4.5 一个容易被忽略的细节:结果之间的互斥约束

因果图里约束关系大多画在“原因”之间,很多教材也只讲原因之间的E、I、O、R,结果之间的M约束讲得很少。但实际业务里结果之间的互斥非常常见。比如“登录成功”和“提示密码错误”不可能同时发生;“支付成功”和“显示支付失败”也不会同时出现。如果不在因果图里把结果的M约束标出来,判定表就会生成出同一列里两个动作同时成立的组合,测试样例根本没法执行。所以画完原因约束后,一定要回头看一眼结果之间有没有互斥关系,补上M约束标记。

5. 因果图的适用范围和最终的一点使用建议

因果图并不是万能的测试设计方法,它有非常明确的适用边界。在我看来,下面几类场景里因果图的投入产出比最高:

  • 业务规则密集的功能模块。比如优惠券、促销活动、权限管理、订单状态流转,这类模块条件多、约束多、结果分支多,几乎是因果图的主场。
  • 需求文档本身逻辑不够清晰的新项目。画因果图的过程本身就相当于一次逻辑走查,能在开发前发现需求矛盾点。
  • 存量系统做回归测试前的核心流程梳理。老系统往往文档不全,逻辑藏在代码里,用因果图把核心流程重新梳理一遍,能有效防止回归时漏场景。

反过来,下面几类场景就别太纠结因果图:纯流程操作型功能、前后端接口的单一参数校验、以及逻辑几乎无分支的展示类页面。这些场景用其他方法可能更顺手,硬套因果图反而浪费时间。

最后再分享一个我的个人使用习惯。我现在在正式项目中,不会每次都用标准的图形符号去画因果图,更多时候会根据需求复杂度灵活换算:简单需求在脑子里过一遍因果逻辑,直接列判定表;中等复杂需求画一张简化的逻辑导图,标注好条件关系,再转判定表;只有那种特别核心、规则特别复杂的模块,我才会完整画一张标准的因果图,把中间节点、约束符号全部画出来,并作为测试设计文档的一部分提交评审。这个方法我用了很久,既能保证测试设计的质量,又能控制日常工作的效率成本。

因果图是一个看上去不那么“高技术含量”、但实际上非常能检验测试设计功力的方法。它逼着你用结构化思维去理解需求,而不是上来就埋头列用例。如果你发现自己写用例总在反复修改、组合场景经常漏,不妨试试从因果图入手,把逻辑关系先捋清楚。磨刀不误砍柴工,这个时间花得值。

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

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

立即咨询