ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

因果图法实战:从逻辑拆解到测试用例设计

因果图法实战:从逻辑拆解到测试用例设计 软件测试这行做过几年的人都有体会越是功能复杂的模块测试用例设计越容易翻车。输入条件一多逻辑关系一绕靠等价类、边界值一个个穷举根本不现实到头来漏测的关键场景全在那些交叉组合里。因果图法就是专门处理这种问题的黑盒测试用例设计方法——它把需求里的“因”和“果”抽出来用图形梳理逻辑关系再把关系转成一张判定表最后从判定表直接映射出测试用例。它最大的价值不在于画出多标准的图而在于逼着测试设计人员把需求里所有“如果……那么……”都穷举清楚。这篇博文适合三类人正在学软件测试基础、准备面试的新人因为因果图法几乎是面试必考题被复杂业务逻辑折磨、苦于用例漏测的在职测试工程师还有想把手头测试项目做得更系统化、可追溯的测试负责人。我会结合真实业务场景从逻辑拆解、画图规则、判定表转换到落地技巧一步不落讲清楚。1. 因果图法的本质不是画图而是拆逻辑1.1 从“等类划分”到“因果驱动”用例设计思维的转折我在带项目的时候发现很多初级测试最容易犯一个毛病拿到需求就凭感觉写用例觉得覆盖了正常流程就算完成任务。但实际上一旦涉及多个输入条件的组合这种“感觉流”方案立刻暴露问题。举例来说一个银行转账功能至少涉及账户状态、余额、转账金额、对方账户、支付密码这几个输入条件而每个条件的取值至少有正常和异常两种光排列组合就有几十种再加上逻辑上的互斥、依赖关系用等价类划分和边界值分析根本处理不了。这时就需要因果图法上场。它的核心思路跟等价类、边界值完全不同前两者关注的是单个输入取值而因果图法关注的是多个输入之间的逻辑关系如何决定输出。这种思维转变非常关键。等价类划分是“每个输入都试一遍”因果图法是“不同输入组合起来试一遍”后者更接近用户实际使用的真实场景。1.2 为什么因果关系分析能覆盖“组合爆炸”盲区举个例子很多新手在测试一个“注册功能”时会分别测用户名、密码、邮箱格式正确的情况再把邮箱格式错误单独测一遍。但要是需求里有“用户名为空时后端直接拦截不校验密码”这种限制条件呢如果你只按单条件设计用例永远发现不了“用户名错误且密码错误”时系统可能出现的奇怪行为。因果图法的第一步是找出所有因输入条件第二步是找出所有果输出结果第三步是分析它们之间是否存在“非”“或”“与”之类的逻辑关联。这一过程实际上是形式化建模把模糊的自然语言需求变成了确定性的逻辑表达式。做过测试的人都有这种体会需求文档里写“当用户输入错误时系统给出提示”但哪种错误优先提示多种错误同时存在时提示哪一条不画因果图根本发现不了这种规则节点。我想强调的是因果图法最大的贡献不是画出了图而是逼着测试人员去关注需求里没说清楚的地方然后去确认。这比任何用例生成算法都重要。2. 核心概念拆解因、果、关系、约束到底各自扮演什么角色2.1 因和果别小看这两个字找错就全盘皆输因果图法里有几个核心术语虽然很简单但在实际项目中经常有人用错。**因Cause**指输入条件是用户能够操作的或者系统接收到的外部条件**果Effect**指输出结果是系统对输入条件做出的反应。注意有些“因”并不是键盘输入而是系统环境状态比如“用户登录状态”“网络状态”“账户余额”都算因。有些“果”也不一定是界面上的提示比如“写日志”“发送短信验证码”“跳转页面”都算果。这里给一个我在实际项目中常用的判断标准如果这个条件可以被测试数据直接控制那它就是因如果这个结果可以通过页面、数据库或接口响应直接观察到它就是果。举例来说在“用户登录”场景中“输入正确的用户名”“输入正确的密码”是因“登录成功”“提示用户名不存在”是果。但“用户名是否存在”这个条件算因还是算果严格来说它受前因影响但我们在设计测试时通常把它当作因来处理也就是先把用户名为空、不存在、存在这三种情况都列出来然后分析它与密码的关系。2.2 四种逻辑关系恒等、非、或、与一次讲透逻辑关系是因果图的骨架常见的有四种。恒等Identity条件满足时结果一定出现用“—”表示例如“点击登录按钮”这个因对应“发起登录请求”这个果。我的经验是恒等关系虽然最简单但却是很多“隐藏需求”的雷区。有些恒等关系在文档里根本不会写比如“用户点击了购买按钮”系统就肯定要创建订单吗不一定可能因为库存不足而不创建。所以恒等关系必须建立在“没有其他中间条件”的前提下。非Not条件不满足时结果才出现用“~”表示。典型场景是“密码错误”这个因导致“不允许登录”的结果。非关系是初学者最容易漏掉的因为人的大脑习惯性地从正面思考问题很少去关注反例。我带的实习生经常问“正常流程不是已经测完了吗为什么还要专门反向考虑”做测试就是得把每个因的反面都当成一个独立的组合项。或Or多个条件中至少一个成立结果就出现用“∨”表示。例如“用户名不存在或密码错误”导致“登录失败”。需要注意实际需求里的“或”关系有时候是不明确的。需求文档可能写“手机号和邮箱均可用于登录”这里的或其实是“两个输入都不能为空”的并发关系而不是逻辑上的“或”。建议遇到这种情况跟产品或开发确认不要自己拍脑袋。与And多个条件同时成立结果才出现用“∧”表示。比如“余额充足且支付密码正确”才能“支付成功”。这是最常见的组合场景也是测试用例数量增长最猛的地方。每多一个与条件用例数就多乘一个分支。为了让你直观理解这四种关系我整理一个速查表测试面试里也经常用到关系符号逻辑含义典型场景失败条件恒等—因成则果成点击按钮→触发点击事件因不成果不成非~因不成则果成密码错误→登录被拒因成果不成或∨任一因成则果成手机号或邮箱为空→提示错误所有因都不成与∧所有因成则果成输入正确账号且正确密码→登录成功任一因不成2.3 五种约束关系E、I、O、R、M这才是因果图法的精髓仅仅有逻辑关系还不够因为在实际业务中输入条件和输出条件之间还会互相限制。这就是因果图法区别于简单逻辑判断的关键约束关系。我总结一份约束速查表约束类型英文全称含义业务场景举例E互斥Exclusive多个条件中最多一个成立支付方式微信/支付宝/银行卡只能选一种I包含Include多个条件中至少一个成立地址必填手机和座机至少填一个O唯一One and only one多个条件中有且仅有一个成立性别选项男/女/保密只能选一个R要求Required某条件成立时另一条件必须成立勾选“同意协议”时必须填写“用户姓名”M屏蔽Masked某条件成立时另一条件一定不成立选择“无需配送”时“配送地址”不可输入我需要特别解释一下E和O的区别这两个在实际应用中最容易混淆。E互斥允许多个条件一个都不出现比如用户既不选微信也不选支付宝直接关掉支付页面但O唯一要求必须有一个且只能有一个比如用户查看订单状态时系统一定会给一个准确状态不可能不给状态。O约束在业务逻辑上等价于“E约束加一个必选条件”如果你在设计用例时想简化可以把O约束拆成E约束加一个“至少一个成立”的规则。M约束是我在支付系统测试中最常用到的。很多测试新手想不通为什么“选择无需配送”这条因成立时“填写配送地址”这个因必须不成立。这是业务规则不是逻辑推导出来的必须通过约束来表达。如果漏掉M这类约束生成的判定表里就会有很多“不可能出现”的组合白白浪费执行时间。2.4 为什么因果图法大多需要“中间节点”以及如何处理它们除了因和果因果图里还有一类特殊的节点——中间节点中间态。它既不是原始输入也不是最终输出而是在因果链上产生的中间结果。我在设计“注册并登录”流程时经常会有“验证通过”这个中间态它由用户名唯一性、密码复杂度、邮箱格式三个因决定然后中间态又会进一步决定最终结果“注册成功并自动登录”。中间节点是很多人理解因果图的卡点。它的存在是因为业务逻辑往往不是一层“因到果”而是多层的。没有中间节点因果关系图会画成一张密密麻麻的网谁也看不懂。处理原则很明确当多个因共同作用产生一个临时结果而这个临时结果又作为上层逻辑的“因”时就把它独立成一个中间节点。这样做能让图保持层次感也方便后续生成判定表时分层处理。3. 因果图法实战全流程从需求文档到可直接执行的测试用例3.1 实战案例设计一个“订单支付”功能的核心规则测试用例光说理论不好理解这里我做一个“订单支付”功能的真实项目案例。需求描述如下订单金额必须大于0元。账户可用余额必须大于等于订单金额。支付密码必须输入正确。如果上述三个条件都满足支付成功系统扣减余额并生成支付流水。如果余额不足系统提示“余额不足”不允许继续支付。如果支付密码错误系统提示“支付密码错误”不允许继续支付。如果支付密码错误次数超过5次账户支付功能被锁定。注意这个需求里有一个逻辑节点非常关键“支付密码错误次数超过5次”会导致“账户锁定”而账户一旦锁定即使密码正确也无法支付。这就是一个典型的隐藏约束也是测试设计时容易漏掉的场景。现在开始用因果图法拆解。第一步列出因和果。因订单金额大于0余额大于等于订单金额支付密码正确错误次数不超过5次果a. 支付成功扣减余额并生成支付流水b. 提示“余额不足”c. 提示“支付密码错误”d. 账户支付功能被锁定第二步分析逻辑关系与约束。因1金额大于0是其他所有逻辑的前提如果因1不成立后续流程不触发。这个可以看作“M约束”如果金额无效屏蔽后续所有操作。因2成立与否直接决定果b是否出现。因3成立与否直接决定果c是否出现。当因2、因3都成立且因4成立时果a出现。因4不成立时无论因3是否正确直接走向果d且后续支付失败。这里需要画一个因果图最开始只有文字对于简单场景还能撑住但逻辑一多没有图形辅助很容易漏。我建议在纸上或者白板上先把因放在左边果放在右边然后把上面的关系用直线连起来把约束关系用虚线标出来。画图时有个技巧先把“与”关系的节点合并为一个中间节点比如因2和因3同时成立合并成一个中间节点“支付前置条件满足”然后再连接到果a。这样图会清爽很多。3.2 从因果图转判定表条件桩、动作桩与规则合并因果图画完之后下一步是转成判定表。这是整个方法里最机械也最关键的一步因为测试用例最终是从判定表里映射出来的。判定表的构成很简单左侧是条件桩和动作桩右侧每一列代表一条规则。我以这个支付功能为例把每个因当作条件每个果当作动作列出所有组合。4个条件每个条件有“成立”和“不成立”两种状态理论上有16种组合。如果资金目前条件只是2个那就2的2次方等于4个组合很容易穷举完。但这里明显不是这样4个条件16种组合如果再加上订单金额不合法、错误次数为0等多个分支组合数量会膨胀。因此我的习惯是先写一个不经过约束筛选的全量组合表再逐条根据约束删掉不可能存在的组合。比如因1不成立订单金额不大于0后面所有操作都失去意义那么因1为“不成立”时无论如何都不会出现支付成功、余额不足、密码错误等提示这些规则可以直接合并删除只保留一条“参数校验失败”。同理当因4不成立时错误次数超过5次无论因3是否正确都不会出现“密码错误”的提示而是直接出现“账户锁定”的结果所以这部分规则也合并。以这个逻辑整理出的判定表大致如下规则序号因1金额0因2余额充足因3密码正确因4错误次数≤5预期动作1N---参数校验失败提示订单金额无效2YN-Y提示余额不足3YYNY提示支付密码错误4YYNN账户锁定不提示密码错误5YYYN账户锁定6YYYY支付成功注意规则4和规则5看起来都是“账户锁定”但触发路径不同。规则4是“密码错误且次数超过5次”规则5是“密码正确但次数已经超过5次”。测试时这两条的入口路径完全不同不能合并成一条——规则4一般是从密码输入错误的页面持续尝试触发锁定规则5是锁定之后用正确的密码试图解锁。业务逻辑上它们理应被区分开。这个例子已经体现了判定表的强大之处通过一行行规则把需求逻辑变成可以执行的测试预期。我们不需要在拿到需求时就拍脑袋想到“密码正确但账户已锁定”这种场景因果图引导我们一步步把组合列全然后筛选、合并最后自然得到这个结果。这种“推导出测试场景”的感觉比靠灵感设计用例要踏实得多。3.3 判定表的具体测试用例映射方式有了判定表测试用例的生成就有了依据。映射原则是每个条件组合即每一列规则对应一组测试输入数据。一个规则可能有多个取值只要属于同一个判定维度的取值可以设计成不同用例但预期结果不变。比如规则6的因1是“金额大于0”实际金额可以设计成1元、0.01元、10000元、1999.99元这其实已经变成了边界值的活了。我的建议是因果图法负责把逻辑覆盖穷尽等价类和边界值负责把取值覆盖穷尽两者要配合使用不要对立。测试用例的具体格式可以直接套用用例编号测试标题前置条件测试数据操作步骤预期结果TC-PAY-001金额无效时阻断支付未输入金额金额0输入金额0元点击支付提示“订单金额无效”不进入支付流程TC-PAY-002余额不足时支付失败余额50元订单金额100元金额100余额50尝试付款提示“余额不足”TC-PAY-003支付密码错误余额100金额50密码错误输入错误密码点击确认提示“支付密码错误”TC-PAY-004密码错误次数超过5次余额100金额50密码错误5次连续5次输入错误密码账户锁定不再提示密码错误TC-PAY-005账户锁定后正确密码也无法支付连续错误密码次数已达5次密码正确输入正确密码支付失败提示账户已锁定TC-PAY-006正常支付成功余额100金额50密码正确输入正确密码确认支付成功余额扣减50生成支付流水在实际项目里我会特别关注TC-PAY-005因为开发经常会漏掉“锁定后无论密码是否正确都拒绝”的判断逻辑这个用例通常真能测出bug来。这也是因果图法的典型价值不是靠运气发现缺陷而是靠逻辑推导发现缺陷。3.4 当因的个数较多时如何控制判定表规模有项目经验的人应该都知道只要因的个数超过6个全量组合的判定表会立刻爆炸。6个因乘两种状态等于64种组合8个因等于256种组合如果每个因还有多个取值直接原地解散。这里我讲讲在实际工作中控制判定表规模的乱办法也是正经办法。第一种是合并等价条件。找出那些“同时成立或同时不成立”的因合并成一个条件。比如“已登录”和“已授权”如果在业务流程中总是同时成立就可以合并为“会话有效”。这种合并要谨慎如果两个因之间有隐藏差异说明需求本身不清晰要从源头确认。第二种是剔除不可能组合。这依赖于约束关系。比如E约束下多个因同时成立的情况就不可能出现直接删掉。很多测试新手忽略这一步骤导致判定表里一堆无效规则然后对着密密麻麻的Excel表格不知所措。第三种是分层次拆分判定表。当逻辑链条很长时用中间节点分割成多个判定表。比如先做一个“前置条件校验”的判定表再做一个“支付执行”的判定表这样每张判定表的因数量不超过4个组合数可控而且层次清晰。我在银行核心系统测试中经常使用第三种方式。银行交易的逻辑链路特别长一笔转账涉及账户验证、风控校验、清算路由、额度限制等多个阶段每个阶段的输入条件不同输出结果也不同。如果把这些全部塞进一张因果图图根本没办法看。合理的方式是拆成多张图、多张判定表每一阶段验证完再进入下一阶段测试设计才能层层把关。这种方式在面试中如果主动提出来绝对是个加分项。4. 常见问题与排查技巧实录4.1 为什么我画出来的因果图没人看得懂是哪里出了问题这是我在指导新人时最常遇到的情况。画出来的因果图密密麻麻连自己都解释不清。出现这种情况大概率是因为没有使用中间节点。正确的方式是分层把“因”放在最左列把“中间节点”放中间把“果”放在最右列连线保持清晰禁止交叉。如果不得已有交叉线说明这一层节点个数太多需要再加一层中间节点。第二个原因是把逻辑关系和约束关系混在一起画。实践中我习惯把逻辑关系用实线表示约束关系用虚线标注或直接写在旁边的注释里。这样阅读起来一眼就能看出哪些是分支逻辑哪些是条件限制。规范不一定要严格照搬ISO文档标准但必须保证团队内可读。我见过太多次“画了因果图比没画还难懂”的失败案例根源就是没有遵守可读性原则。第三个原因是图里混了太多业务细节。因果图法是一套测试设计方法它只关心“条件和结果之间的逻辑”不关心实现细节。比如支付密码加密方式是AES还是RSA这不是因果图该画的东西放到用例前置条件里记录就行。因果图里只出现条件、中间节点、结果、逻辑关系、约束关系五类元素其它一律不画。4.2 因果图法在接口测试和自动化测试中能不能用经常有做接口自动化测试的同事问我现在都是自动化执行用例了因果图法这种“手工设计”的方法还有意义吗我的回答是不但有意义而且接口测试更需要因果图法。接口测试的特点是参数很多参数之间的约束关系也很复杂。以支付接口为例它可能要接收订单编号、支付方式、支付金额、支付密码、设备指纹等多个参数而参数之间存在验证顺序和互斥关系。如果直接写自动化脚本每个参数组合都要写一条用例脚本数量会失控。但如果先用因果图法整理出参数之间的逻辑关系再生成精简的判定表然后再编写自动化脚本脚本的可维护性会大幅提升。我建议的做法是把因果图/判定表整理成一份独立于自动化脚本的测试设计文档然后在自动化测试框架里用数据驱动的方式读取这些判定规则。这样当需求变更时先修改判定表再调整对应测试数据自动化脚本本身可能只需要改极少的地方。这种“测试设计驱动自动化”的思路能让自动化测试不再是临时堆砌的脚本。4.3 因果图法在面试中怎么回答才出彩因果图法是软件测试基础知识里的高频面试题但我发现很多候选人的回答都停留在“画图、转判定表、生成用例”这三步上完全无法体现自己的实战理解。如果你能补充下面几点面试官一定会眼睛一亮第一主动提及“因果图法最大的瓶颈在于条件组合爆炸”并说明你会通过约束关系筛选、合并等价条件、分层拆解三种方式控制规模。这能证明你有实战项目经验而不只是背书。第二能主动指出“因果图法和其他黑盒方法不是替代关系而是互相配合的关系”。比如先用因果图法梳理逻辑再用判定表映射用例最后用边界值分析法确定每个条件的边界数据。这能体现你的方法论有体系是全局视角。第三能结合自己实际项目举例说明通过因果图法发现了哪些隐藏缺陷。比如我在支付项目中用因果图法发现“账户锁定后正确密码依然被拒”这种隐藏场景这类案例自带真实感比背概念有说服力得多。4.4 给新人的实战避坑清单我最后整理一份避坑清单都是我在项目里踩过或看别人踩过的真实问题不要一上来就画图。先跟产品经理或开发确认需求把所有输入条件和输出结果都列成清单如果有歧义就先解决歧义再画图。不要忽略约束关系。只画与或非不画约束相当于只看到冰山一角生成出来的判定表必然不完整。不要直接拿因果图当测试用例。因果图是中间产物用例一定要从判定表导出否则逻辑覆盖性没有保障。不要只做一次。当需求变更时要重新审视因果图和判定表更新后再调整自动化测试脚本否则测试设计和代码就会出现漂移。不要为了用而用。如果业务场景只有两三个输入条件且逻辑简单优先用决策表或直接写用例因果图法会显得多余。它最适合的场景是条件数量多、逻辑层次深、规则复杂的模块。我在实际项目里被因果图法救过不止一次印象最深的是之前做一个充值活动模块优惠规则层层嵌套产品经理自己都说不清满减叠加顺序。当时我就是拉上产品经理围在白板前把因和果一个个列出来再逐条确认关系画到后半段产品经理自己都发现了需求文档里的逻辑漏洞。那一刻我就觉得因果图法的价值远远不止于“设计测试用例”它本身就是一种需求澄清工具。最后再分享一个小技巧画因果图初期尽量用白板或粗笔在纸上画不要急着打开工具软件。因为画图的过程是不断修改逻辑的过程铅笔和橡皮比鼠标键盘高效得多。等图稳定了再誊到Excel或者在线绘图工具里归档这才是正确的工作节奏。
返回列表