
做测试这些年我见过不少人对测试用例设计方法的态度是两极分化的一边觉得等价类划分和边界值分析就够了另一边则一上来就研究正交实验、Pairwise、全组合覆盖恨不得把每个功能都压出上万条用例。但真到了那种条件多、组合复杂、输出又互相牵扯的需求面前很多人会突然发现自己连需求逻辑都没真正理顺。这个场景就是因果图的用武之地。因果图也叫因果分析法是黑盒测试里专门用来分析输入条件组合和输出结果之间逻辑关系的测试用例设计方法。它不是新鲜东西却在今天的复杂业务系统里依然非常能打尤其是优惠活动、订单状态机、风控规则这类逻辑密集型场景用因果图来设计测试方法往往能比纯靠经验硬想要系统得多。这篇博文我把这套方法从原理到实操、从画图到转判定表、再到生成测试用例的完整过程结合我自己项目里踩过的坑讲清楚。适合刚接触测试用例设计的新人也适合那些觉得“自己会用因果图但总觉得差点意思”的从业者。1. 因果图这套测试方法的定位与核心价值1.1 等价类和边界值真正管不了的是“组合关系”先说说传统的等价类划分思路把输入域按是否产生相同处理结果划分成若干个等价区间再从每个区间里挑一个代表值来测试。边界值分析则是盯着这些区间的边界取值。这两个方法都有一个共同的底层假设我测的是单个输入条件的取值和这个条件对应的处理逻辑之间的关系。可现实中的需求往往没法拆成单条件看。我举一个自己实际测过的例子。某个活动系统里用户要获得折扣需要同时满足“是会员”“首单”“商品在活动范围内”三个条件任何一个不满足都不能享受折扣而且还要给出不同的拒绝原因。三个条件每个条件取“满足”和“不满足”两个值组合起来就是2的3次方一共8种情况。这还只是“与”的关系。现实里往往还有“或”的关系、“非”的关系条件之间还存在互斥、唯一、要求、屏蔽等约束。这些关系单靠等价类和边界值根本没法系统梳理出来。我见过很多测试人员的做法是对着需求文档先把正常流程跑通再把能想到的异常情况挨个试一遍。这种“脑图式”测试遇到逻辑简单的模块还行一旦遇到五六个条件互相嵌套的需求漏测几乎是必然的。因为人的短时记忆根本扛不住这么多组合。因果图的价值就是把这些组合关系用图形化方式显式地画出来让逻辑漏洞自己暴露出来。1.2 因果图核心逻辑从“看单个输入”到“看输入组合”因果图的完整步骤简单说就是六步。第一步从需求中找原因也就是输入条件注意是能够独立影响结果的输入条件。第二步找结果也就是输出动作或者状态变化。第三步画出原因和结果之间的逻辑关系常见的是恒等、非、或、与这四种。第四步标注条件之间的约束关系也就是那些在业务语义上不可能同时出现、或者必须同时出现的组合。第五步把因果图转成判定表。第六步从判定表里设计测试用例。这里有个关键点要强调因果图本身不是直接生成测试用例的它生成的是判定表测试用例从判定表里来。因果图和判定表通常是一套组合拳。画因果图的过程本质上是把需求文档里的自然语言“翻译”成一张逻辑关系网而转判定表的过程则是把这张网变成穷举条件组合的表格。表格一旦出来用例就几乎是“看得见”的了。我自己的习惯是因果图画完以后中间那个判定表才是真正拿去评审的产物。因为开发、产品都能看懂表格但未必习惯看图。所以因果图更像是帮我自己思考用的内部工具判定表才是对外输出。1.3 什么场景优先用因果图什么场景别硬用因果图不是万能的我总结了几条判断标准。适合用的场景大概是这几类第一输入条件多输出结果也多而且条件之间的不同组合会触发不同输出第二规则引擎类业务比如风控、优惠、审批流、计费规则这些业务逻辑天生适合画因果图第三模块逻辑复杂到你自己都觉得“不画下来肯定漏”的程度。这类需求用因果图效果立竿见影。不适合硬用的场景也很多。逻辑只有两三行条件很少的模块因果图就是过度设计等价类加边界值几步就搞完了。还有纯流程型业务比如一个页面跳转依赖操作顺序而不是条件组合这种更适合场景法或者状态迁移法。因果图擅长分析的是“组合爆炸”问题不是“状态流转”问题。方法选型的本质是投入产出比别把因果图当成万能锤。2. 因果图的基本语法逻辑关系和约束关系2.1 四种基本逻辑关系恒等、非、或、与因果图里的原因和结果都用节点表示原因通常在左边结果在右边中间用连线加逻辑符号连接。最基本的逻辑关系有四种对应数字电路里的四种门电路。虽然听着有点理工科味道但其实特别生活化。第一种是恒等关系。意思是原因成立结果就成立原因不成立结果就不成立。比如电商里“附近有门店”这个原因对应“展示门店列表”这个结果。有门店就展示没门店就不展示这就是恒等关系。第二种是非关系。意思是原因成立时结果反而不成立。比如“库存充足”这个原因对应的结果可以是“提示商品可售”。库存充足时不会提示不可售库存不足时反而会提示这两个状态就是非的关系。第三种是或关系。多个原因里只要有一个成立结果就成立。比如手机解锁指纹识别成功或者密码输入正确任何一个成立都能解锁。只有当所有原因都不成立时结果才不成立。第四种是与关系。多个原因必须全部成立结果才成立。前面说的“是会员、首单、商品在活动范围内”三个条件全部满足才享受折扣就是典型的与关系。这四种关系是因果图的骨架。我自己的经验是画图时对着需求逐句看把每个“如果...那么...”的句子拆开判断是哪种逻辑连接先标出来再连线。别怕画得乱关键是逻辑要准。2.2 五种输入状态约束E、I、O、R、M除了原因和结果之间的逻辑关系原因之间还有约束关系。这是因果图里特别容易被人忽略的部分但恰恰是最能体现对业务理解深度的地方。约束关系一共五种我用一张表说清楚。约束符号约束名称逻辑含义典型业务场景E互斥Exclusive多个原因中最多只能有一个为真支付方式支付宝、微信、银行卡同一订单只能选一种I包含Inclusive多个原因中至少有一个为真风控系统三种风险检测至少跑其中一种才能放行O唯一Only多个原因中有且仅有一个为真订单状态待支付、已支付、已取消同一时刻只能有一个R要求Require一个原因为真时另一个原因必须为真勾选“同意协议”后“提交订单”按钮才可点击M屏蔽Mask一个原因为真时另一个原因必须为假勾选“不使用优惠券”时优惠券选择区域不可输入我在实际项目里吃过亏。有一回测一个注册流程条件是“手机号”、“验证码”、“同意协议”三个输入我一开始画因果图时没加“要求”约束判定表里就出现了一个“未勾选同意协议却能提交”的规则列。虽然我当时心里知道这个组合不可能但没在图里标注出来导致判定表凭空多出好几列无效用例。加了R约束之后这些列直接被删掉测试范围一下子清晰了。2.3 画图的工具选择和个人习惯工具方面白纸、Excel、Visio、draw.io都行。我个人的建议是别急着上工具先用白纸画。因为画因果图的过程本质上是思考过程白纸没有格式负担随手就能勾。我一般在白纸上画完草稿理清楚逻辑关系后再用draw.io整理一份电子版用于评审和归档。draw.io免费、导出的XML方便做版本管理团队协作时很实用。还有一个习惯我觉得值得分享画图时给每个原因和结果都起一个能看懂的名字比如“用户是会员”“享受折扣”别用C1、C2、E1这种纯代号。虽然书上示例喜欢用编号但实际评审时写“用户是会员”比写“C1”直观得多大家讨论起来也不用对着编号表来回翻。我一般是在节点旁边直接写中文名编号放在后面作为辅助。3. 从需求到测试用例的完整实操以ATM取款为例3.1 需求拆解把自然语言变成原因和结果光讲理论容易飘我拿一个非常经典但又足够说明问题的案例走一遍全流程。需求如下ATM取款时系统依次校验卡片状态、取款金额是否为100的正整数倍、账户余额是否充足。如果卡片状态异常提示“卡片状态异常”不继续后续校验如果卡片正常但取款金额不是100的正整数倍提示“取款金额应为100的整数倍”如果卡片正常、金额合法但余额不足提示“余额不足”所有条件都满足则出钞成功。先把原因和结果拆出来。原因C1取款金额是100的正整数倍C2账户余额充足C3银行卡状态正常结果E1出钞成功E2提示“取款金额应为100的整数倍”E3提示“余额不足”E4提示“卡片状态异常”这里有个关键经验拆原因时要注意合并取值范围。比如“取款金额是100的正整数倍”这个原因其实已经把“正数”和“100的倍数”两个要求合并了。因为如果不合并条件一多判定表会膨胀得非常快。先用等价类把条件取值范围做一个合理抽象这是因果图实操里的第一步。3.2 画因果图连线过程就是逻辑梳理过程原因放左边结果放右边。这个案例的逻辑关系是三层。C1与C2同时为真时E1出钞成功成立这里是一个“与”关系可以画成从C1、C2引线到一个与门再通向E1。C1不为真时E2提示金额错误成立这是对C1取“非”之后通向E2。C1为真且C2不为真时E3提示余额不足成立。C3是否正常决定了整个流程是否继续C3不为真时直接触发E4提示卡片状态异常而且无论C1、C2如何都不会触发其他结果。画图的时候我习惯把C3放在最上方因为它优先级最高。中间用带逻辑门的连线把节点连起来最后检查一遍看有没有原因漏画看每个结果是否都有原因指向看约束关系有没有标注在原因之间。这个案例里C3为假会屏蔽掉后面的所有校验所以可以理解为C3与C1、C2之间存在一种类似“屏蔽”的逻辑但因为是原因和结果之间的流程关系我更倾向于在判定表里通过优先级规则来体现。这里要提一句画因果图没有标准答案只要逻辑关系正确图形长什么样并不重要。我见过有人把所有逻辑都浓缩在一张图上也见过有人拆成几张子图分开画。都合理。关键是想清楚优先级因为后面的判定表设计完全依赖这个。3.3 转判定表把所有组合摆到台面上因果图转判定表是整条链路里最机械也最需要细心的一步。判定表的结构是左边上方是条件桩列出所有原因左边下方是动作桩列出所有结果右边每一列代表一条规则列出该规则下各条件和各动作的取值。这个案例有三个条件理论上2的3次方一共8种组合。我完整列出判定表如下。规则编号C1金额为100的倍数C2余额充足C3卡片状态正常E1出钞成功E2提示金额错误E3提示余额不足E4提示卡片异常1000000120010100301000014011010051000001610100107110000181111000这个表格刚列出来的时候第一反应是规则1、3、5、7这四列的预期输出长得一模一样都是E4为1。原因是C3为假时后面的校验根本不会执行C1、C2取什么值都不影响结果。这就是判定表化简的机会。化简之后规则被合并成四条。第一条C3为0无论C1、C2如何预期结果是E4。第二条C3为1且C1为0无论C2如何预期结果是E2。第三条C3为1、C1为1且C2为0预期结果是E3。第四条C3为1、C1为1且C2为1预期结果是E1。我一直觉得判定表化简是因果图方法里最有价值的一步。它直接告诉测试人员我其实只需要测四条规则对应的场景就够了。没有化简之前你可能会傻乎乎地按8列去设计用例但那其中有4列测的是同一个结果纯属重复劳动。化简的本质是找到真正影响输出的条件组合。3.4 确定预期输出和测试用例设计判定表化简之后每条规则对应一条测试用例几乎可以一条一条直接翻译成用例。用例一卡片状态异常比如挂失或消磁输入任意取款金额预期结果是提示“卡片状态异常”。这条用例覆盖化简后的规则一。用例二卡片状态正常输入金额150元预期结果是提示“取款金额应为100的整数倍”。覆盖规则二。注意这里输入金额要选一个非100整数倍的正数同时还要保证余额充足这样才能确认是金额问题而不是余额问题。用例三卡片状态正常输入金额200元但账户余额不足预期结果是提示“余额不足”。覆盖规则三。同样要保证金额合法余额不足这样预期输出才会指向余额这一层。用例四卡片状态正常输入金额200元账户余额充足预期结果是出钞成功。覆盖规则四。到这里这套用例已经基本覆盖了整个逻辑分支。但实际工作中我还会再加几条补充用例比如输入金额为0、负数、100.5元这类边界组合。这些补充用例不属于判定表核心规则但属于等价类和边界值要处理的内容。因果图给出的是逻辑骨架等价类边界值负责把单个条件的边界细节填上。这套组合用下来覆盖度才完整。4. 常见问题与排查技巧实战中的真实分享4.1 画了因果图还是漏场景问题出在哪里这是我在带新人时最常被问到的问题。明明已经按步骤把因果图画出来了为什么评审时还是被产品指出漏了场景我复盘过几次主要原因集中在四个地方。第一是原因找不全。很多隐含条件没有进图。比如我前面案例里的“卡片状态正常”这个原因需求文档里可能只是一句“仅限正常状态银行卡取款”但新手往往会漏掉这种背景条件。第二是结果定义太粗一个结果里藏了多种情况。比如只写“提示错误”但错误类型有好几种对应的系统和用户行为完全不一样必须拆开。第三是漏标约束关系结果就是判定表里出现一堆业务上不可能的组合既干扰判断又浪费用例量。第四是没有定义结果之间的优先级。多个结果同时满足时系统到底展示哪个这个不明确用例的预期输出就是模糊的。遇到这种情况我的排查方法是拿到一份已经画好的因果图依次问三个问题——需求里每个条件是否都进图了每个输出是否都有明确且唯一的预期条件之间有没有业务上不可能同时出现的组合问完这三轮大部分漏的场景都能找回来。4.2 判定表规则爆炸怎么办条件数量一多判定表的行数指数级增长。五个条件就是32行六个条件就是64行如果是八个条件就是256行。这时候如果还想着全部展开用例数量会让团队直接崩溃。我有几个降维手段。第一个手段是前置等价类抽象。每个条件不要在判定表里展开全部取值只用“有效”“无效”两个状态或者最多三个状态这样条件维度就被压下来了。第二个手段是活用约束关系。E、I、O、R、M这些约束直接把不可能组合整行删掉规则数量能砍掉一大截。第三个手段是引入Pairwise。当条件组合实在太多时可以用Pairwise方法做两两组合覆盖是一种合理的取舍策略。但要注意Pairwise只保证任意两个条件的组合被覆盖到三个条件以上的组合效应可能漏掉使用时要评估风险。我在实际项目中多采用这样的策略核心规则用因果图加判定表保证全覆盖非核心、低风险的条件组合用Pairwise或者经验补充。质量目标和投入成本之间需要做平衡这也是资深测试和初级测试在方法选型上的一个明显差别。4.3 因果图、判定表、场景法怎么配合使用经常有人把因果图、判定表、场景法放在一起比较其实它们并不互相替代。场景法关注的是业务流程里主路径和备选路径的流转适合回答“用户按什么顺序操作会走到哪一步”的问题。因果图和判定表关注的是某个判断节点里条件组合和输出的映射关系适合回答“这个节点的规则到底有没有漏洞”的问题。我自己的组合打法通常是这样先用场景法把业务流程的主路径、备选路径和异常路径理出来然后找到路径中那些“条件特别复杂”的判断节点在这个节点上用因果图展开分析。比如订单提交流程里支付方式、优惠券、用户等级、库存状态这些条件会汇聚在一个判断节点上这个节点就非常适合用因果图单独深挖。路径归路径规则归规则两层分开处理效率和准确率都高。4.4 因果图与AI agent辅助测试的协作心得最近AI agent辅助测试设计是个热门话题。我自己也试过让大模型帮我生成测试用例经历过几次发现直接让AI“凭经验”生成用例效果好坏全看运气。但如果先把因果图转成结构化的规则描述再喂给AI效果会明显好很多。因为因果图实际上把需求的逻辑骨架给显式化了AI不需要自己从自然语言里猜测规则只需要基于清晰规则去枚举边界、补充异常场景就好。我现在的工作流是画完因果图、做完判定表之后把原因、结果、约束、优先级整理成一条条结构化语句交给AI agent去扩展边界值用例。比如“如果C3为假则输出E4且C1、C2不参与后续判断”这种描述AI能很准确地生成“卡片状态异常输入金额0元”“卡片状态异常输入金额100元”等组合。这种协作方式把AI的优点和人的逻辑梳理能力结合起来了。你没法指望agent理解业务隐含优先级但你可以把隐含规则显式喂给它这正是因果图的价值所在。4.5 踩过的一些坑和规避建议再说几个自己踩过的具体坑。第一个坑是画因果图时把无关条件也画进来了。有时候需求文档描述特别长容易把一些不影响当前模块的处理条件也当成原因。我一开始画图时什么都想往图里塞结果图越来越复杂判定表行数翻倍但很多规则对应的结果完全一样白白增加工作量。现在我的做法是先问自己“这个条件如果不满足输出会变化吗”不会变化的就不画。第二个坑是判定表里结果列的预期值没有明确优先级。像前面的例子如果卡片状态异常和金额不合法同时存在预期到底应该是什么这种问题在需求文档里经常写得含糊。我的处理方式是尽早找产品和开发确认优先级并在判定表里显式标注“本列结果按卡状态、金额、余额顺序判断”。如果不确认测试用例的执行结果可能没有bug也会被判为bug纯属内耗。第三个坑是化简过度。把规则合并得太狠合并到用例场景看不清了。化简的目的是删掉对输出没有影响的条件维度而不是把可读性也一起删掉。我一般会保留化简前后两种形式化简后的用于设计用例化简前的用于和开发、产品对齐逻辑。两张表放在一起讨论起来效率非常高。5. 一些个人体会最后说点我个人在实际使用中的体会吧。因果图这套方法看着老但它其实是个极好的思维工具逼着我把需求里每一个“如果这样就那样”都拆开晾在台面上。画过这么多次因果图我几乎每次都能发现一两个平时凭感觉测根本想不到的组合场景。哪怕到最后不画正式的因果图只把“原因-约束-结果”这个拆解习惯带在身上对写测试用例、评审需求、甚至跟开发和产品battle边界条件都特别有用。如果你也在复杂逻辑测试上栽过跟头我的建议是下次拿到需求先别急着打开测试用例管理工具拿张草稿纸把因果图画一遍。你会发现那个让你觉得很难讲清楚的需求其实远没有想象中复杂。