测试插入与断言覆盖收敛实战 - 第4节

条件覆盖:定义与原理

条件覆盖,说白了就是要把每个判断条件里的原子条件都测一遍。

什么叫原子条件?我举个例子:

if (a > 0 && b < 10) {
    // 业务逻辑
}

这里 a > 0 是一个原子条件,b < 10 是另一个。条件覆盖要求:每个原子条件至少取一次 true、一次 false。

我个人习惯把条件覆盖理解成「拆到不能再拆为止」。你想想看,如果只做判定覆盖,可能只测了 true 和 false 两种组合,但内部的条件到底谁在起作用?不知道。

我在项目中遇到过这样的坑:一个复杂的权限校验逻辑,判定覆盖全过了,结果上线后某个角色死活进不去某个页面。一查,原来是某个子条件从来没被触发过 false 分支。

核心要点:条件覆盖关注的是每个原子条件的真值变化,而不是整个表达式的最终结果。

条件覆盖的粒度

这里有个容易混淆的点——粒度问题。

条件覆盖的粒度,取决于你怎么定义「原子条件」。一般来说:

  • 关系表达式:如 a > 0、name == "admin",每个算一个原子条件
  • 布尔变量:如 isActive、flag,每个算一个
  • 函数返回值:如 checkStatus(),通常也视为一个原子条件

嗯,这里要注意:不要过度拆分。比如 a > 0 && b < 10 里的 a > 0 本身就是一个整体,你没必要再拆成 a 和 0 的比较操作。

覆盖类型 粒度 测试用例数(典型)
语句覆盖 最粗 1~2
判定覆盖 中等 2
条件覆盖 较细 2~n(n为原子条件数)
条件/判定覆盖 更细 2~n
MC/DC 最细 n+1

从表格能看出来,条件覆盖的测试用例数跟原子条件数量直接相关。条件越多,用例越多。

我的经验:实际项目中,条件覆盖通常用在关键模块的接口层或核心算法上。全量条件覆盖成本太高,我一般建议「核心路径全覆盖,非核心路径抽样」。

实战案例:计算复合条件的条件覆盖率

光说不练假把式。我们来看一个真实的例子。

被测代码

public boolean canAccess(String userType, int level, boolean isVip) {
    // 复合条件:三种原子条件组合
    if ((userType.equals("admin") || level >= 5) && isVip) {
        return true;
    }
    return false;
}

这里有三个原子条件:

  • A: userType.equals("admin")
  • B: level >= 5
  • C: isVip

条件覆盖要求

每个原子条件至少取一次 true、一次 false。所以我们需要:

  • A 为 true 的用例 + A 为 false 的用例
  • B 为 true 的用例 + B 为 false 的用例
  • C 为 true 的用例 + C 为 false 的用例

最少需要多少用例?理论上 2 个就够了,但实际往往需要更多。

设计测试用例

用例编号 userType level isVip A B C 结果
TC1 "admin" 3 true true false true true
TC2 "user" 6 false false true false false

检查一下:

  • A: TC1=true, TC2=false ✅
  • B: TC1=false, TC2=true ✅
  • C: TC1=true, TC2=false ✅

两个用例就实现了 100% 条件覆盖!

条件覆盖率 = (已覆盖的原子条件真值组合数 / 总原子条件真值组合数) × 100%

本例:6个真值组合(3个条件 × 2个值),全部覆盖,覆盖率 100%。

条件覆盖率的计算过程

实际项目中,我们通常用工具来算。但手工算一遍有助于理解:

  1. 列出所有原子条件:A、B、C
  2. 列出每个条件的真值表:A(true/false)、B(true/false)、C(true/false)
  3. 统计已覆盖的真值:每个条件至少有一个 true 和一个 false
  4. 计算覆盖率:已覆盖的真值数 / 总真值数

我曾经在一个金融项目中,用条件覆盖发现了支付网关的一个隐藏 bug。那个条件表达式有 5 个原子条件,判定覆盖只用了 2 个用例就过了,但条件覆盖需要至少 6 个用例。结果第 4 个用例就暴露了问题——某个边界条件下,手续费计算逻辑走错了分支。

注意:条件覆盖 ≠ 判定覆盖。条件覆盖 100% 不代表判定覆盖 100%。比如上面的例子,如果只测 TC1 和 TC2,判定覆盖只有 50%(只覆盖了 true 和 false 各一次,但判定结果只有 true 和 false 两种,实际上两个都覆盖了,所以判定覆盖也是 100%——这里要小心,条件覆盖和判定覆盖的覆盖对象不同,不能直接等价)。

条件覆盖的局限性

说实话,条件覆盖也不是万能的。它有个明显的短板:不保证覆盖所有条件组合。

比如上面的例子,虽然每个原子条件都覆盖了 true/false,但组合情况呢?A=true & B=true & C=true 这种组合就没测到。如果这个组合会触发某个特殊逻辑,那就漏了。

所以实际工作中,我通常把条件覆盖作为基础要求,再结合判定覆盖、MC/DC 来补全。别指望一招鲜吃遍天。

知识体系总览

下面这张图帮你理清条件覆盖在整个测试覆盖体系中的位置:

条件覆盖知识体系 测试覆盖技术 语句覆盖 判定覆盖 条件覆盖 原子条件:每个条件独立取 true/false 粒度:关系表达式 / 布尔变量 / 函数返回值 覆盖率 = 已覆盖真值数 / 总真值数 条件覆盖不保证 覆盖所有条件组合 建议结合判定覆盖 和 MC/DC 使用 核心思想:每个原子条件都要「见过世面」

条件覆盖是测试覆盖技术中承上启下的一环。它比判定覆盖更细,但又不像 MC/DC 那样严格。说白了,它就是让你确保每个小条件都被「折腾」过——既当过 true,也当过 false。

我在写测试用例时,习惯先列原子条件清单,再设计用例。这样不容易漏。你也可以试试这个方法。

实用技巧:用 Excel 或思维导图列出所有原子条件,然后打勾确认每个条件都有 true 和 false 的用例。视觉化之后,漏掉的情况一目了然。

可可精品资料库