3. 测试用例设计方法:等价类划分、边界值分析、因果图法、场景法在嵌入式中的应用

做嵌入式测试这些年,我最大的体会是:测试用例设计不是拍脑袋。你想想看,一个嵌入式系统可能有几十个输入参数、上百种状态组合。靠直觉去测,漏掉关键场景是迟早的事。

这一章,我把最常用的四种方法掰开揉碎讲清楚。每种方法我都会结合嵌入式场景,告诉你什么时候用、怎么用、坑在哪。

核心观点:没有一种方法能覆盖所有场景。等价类划分负责“减量”,边界值分析负责“找茬”,因果图法负责“理关系”,场景法负责“串流程”。四者配合,才能构建完整的测试用例体系。

3.1 等价类划分:把无限输入变成有限测试

等价类划分,说白了就是把输入域分成若干“等价”的组。同一组里的数据,程序处理逻辑是一样的。你只需要从每组里挑一个代表来测就行。

我在项目中遇到过这样一个案例:一个温度采集模块,输入范围是 -40°C 到 125°C。如果按每个整数点去测,要测 166 个点。用等价类划分,只需要 3 个:

等价类 范围 代表值 预期结果
有效等价类(正常范围) -40 ~ 125 25 正常采集并上报
无效等价类(低于下限) < -40 -50 返回错误码或默认值
无效等价类(高于上限) > 125 130 返回错误码或默认值

嗯,这里要注意:无效等价类往往比有效等价类更容易发现 bug。我见过太多工程师只测正常情况,结果产品在现场一遇到异常输入就死机。

我的习惯:每个输入参数至少划分 1 个有效等价类和 2 个无效等价类(低于下限、高于上限)。如果参数有特殊格式(如 MAC 地址、IP 地址),还要增加格式错误的等价类。

3.2 边界值分析:bug 最喜欢藏在边界上

为什么边界值分析这么重要?因为程序员写条件判断时,最容易在边界上犯错。比如 if (temp >= -40 && temp <= 125),少写一个等号,边界值就出问题了。

边界值分析的核心原则:取边界点、边界内邻点、边界外邻点。还是以温度采集为例:

边界位置 测试值 说明
下边界 -40 刚好在边界上
下边界内邻点 -39 边界内第一个有效值
下边界外邻点 -41 边界外第一个无效值
上边界 125 刚好在边界上
上边界内邻点 124 边界内最后一个有效值
上边界外邻点 126 边界外第一个无效值

我曾经在一个 UART 通信驱动里,就因为边界值没测到位,导致波特率配置在 115200 时偶发丢数据。排查了两天才发现,是分频系数计算在边界处溢出了。从那以后,边界值分析成了我写测试用例的必选项。

避坑指南:边界值分析不只看数值边界,还要看时间边界、计数边界、缓冲区边界。比如看门狗超时时间、环形缓冲区的读写指针、DMA 传输长度——这些地方都是 bug 高发区。

3.3 因果图法:理清多条件组合的逻辑

当输入条件之间有逻辑关系时,等价类和边界值就不够用了。比如一个电机控制系统:

  • 条件 1:温度正常(T)
  • 条件 2:电流正常(C)
  • 条件 3:急停按钮未按下(E)
  • 结果:电机启动(M)

逻辑关系是:M = T AND C AND E。用因果图法,我们可以画出因果关系,然后生成决策表:

条件/结果 规则1 规则2 规则3 规则4 规则5 规则6 规则7 规则8
T(温度正常) 1 1 1 1 0 0 0 0
C(电流正常) 1 1 0 0 1 1 0 0
E(急停未按下) 1 0 1 0 1 0 1 0
M(电机启动) 1 0 0 0 0 0 0 0

你看,8 种组合里只有 1 种能让电机启动。如果不画因果图,你可能会漏掉“温度异常但电流正常”这种场景。

我个人习惯是:当条件数在 3~6 个之间时,因果图法效率最高。条件太少直接用等价类就行,条件太多(超过 7 个)组合爆炸,建议拆分成多个子功能分别分析。

3.4 场景法:用真实流程串联测试

前面三种方法都是“静态”的——它们关注的是输入和输出的关系。但嵌入式系统是“动态”的,有状态转换、有流程流转。这时候就需要场景法。

场景法的核心是:识别用户的操作路径,覆盖基本流和备选流。以智能门锁为例:

  • 基本流:用户输入正确密码 → 门锁打开 → 用户进门 → 门锁自动上锁
  • 备选流 1:用户输入错误密码 → 提示重新输入 → 连续 3 次错误 → 锁定 30 秒
  • 备选流 2:用户使用指纹开锁 → 指纹匹配 → 门锁打开
  • 备选流 3:低电量报警 → 用户使用机械钥匙开锁 → 更换电池
  • 备选流 4:门锁被暴力撬动 → 触发报警 → 发送通知到手机

每个场景都要设计测试用例。我建议用场景-用例矩阵来管理:

场景编号 场景描述 前置条件 测试步骤 预期结果
S01 正常密码开锁 门锁处于待机状态 输入正确密码 → 按确认键 门锁打开,蜂鸣器响一声
S02 密码错误 3 次锁定 门锁处于待机状态 连续输入 3 次错误密码 第 3 次后屏幕显示“锁定 30s”
S03 低电量开锁 电池电量 < 10% 输入正确密码 门锁打开,同时屏幕闪烁低电量图标

我曾经在一个车载 T-Box 项目里,用场景法发现了 OTA 升级流程中的死锁问题。基本流测了 10 遍都没问题,但备选流“升级过程中网络中断”这个场景,一测就复现了。所以我的建议是:基本流保证功能可用,备选流保证系统健壮。

3.5 四种方法的协同使用

这四种方法不是孤立的。我通常这样组合:

  1. 先用场景法:梳理出用户的操作流程和系统状态转换
  2. 再用因果图法:分析每个场景中的条件组合逻辑
  3. 然后用等价类划分:对每个输入参数分组,减少测试数量
  4. 最后用边界值分析:在等价类的基础上,补充边界测试

下面这张图展示了它们之间的关系:

测试用例设计方法协同框架 场景法 梳理流程与状态 因果图法 分析条件组合 等价类划分 分组减少测试量 边界值分析 补充边界测试 使用顺序与协作说明 1. 场景法识别出所有用户操作路径 2. 因果图法分析每个场景中的条件逻辑 3. 等价类划分对输入参数分组,减少用例数 4. 边界值分析在等价类基础上补充边界测试

3.6 嵌入式场景的特殊考量

嵌入式测试和纯软件测试不一样。有几点我要特别提醒:

  • 硬件约束:有些输入值在软件层合法,但硬件不支持。比如 ADC 采样值,软件定义范围是 0~4095,但硬件实际只能到 4080。边界值要按硬件实际能力来取。
  • 时序约束:嵌入式系统对时间敏感。等价类划分时,要把“时间”也当作一个输入参数。比如按键消抖时间 20ms,边界值要测 19ms、20ms、21ms。
  • 资源约束:内存、Flash、栈空间都是有限的。场景法要包含“资源耗尽”的场景,比如内存分配失败、栈溢出。
  • 中断干扰:因果图法要考虑中断对条件的影响。比如一个条件在判断过程中被中断改变了,结果会怎样?

我曾经踩过的坑:在一个传感器驱动项目中,我用等价类划分把温度值分成了 3 组,边界值也测了。结果现场发现,温度在 0°C 附近时,传感器输出的值会跳变。原因是传感器本身在 0°C 附近有非线性区。从那以后,我养成了一个习惯:等价类划分之前,先看芯片手册,了解硬件的行为特性。

3.7 实战案例:温控系统的测试用例设计

最后,用一个完整的案例把四种方法串起来。假设我们要测试一个温控系统:

  • 输入:当前温度(-20°C ~ 80°C)、目标温度(0°C ~ 60°C)、工作模式(加热/制冷/自动)
  • 输出:加热器控制信号、制冷器控制信号、报警信号
  • 逻辑:自动模式下,当前温度低于目标温度-2°C 时开启加热,高于目标温度+2°C 时开启制冷

第一步:场景法

  • 基本流:设置目标温度 → 系统自动调节 → 达到目标温度 → 保持恒温
  • 备选流 1:温度传感器故障 → 系统报警并关闭输出
  • 备选流 2:目标温度设置超出范围 → 提示错误并保持原设置
  • 备选流 3:手动切换工作模式 → 系统立即切换控制策略

第二步:因果图法

分析自动模式下的控制逻辑:

  • 条件 A:当前温度 < 目标温度 - 2°C
  • 条件 B:当前温度 > 目标温度 + 2°C
  • 结果 X:开启加热器
  • 结果 Y:开启制冷器
  • 逻辑:X = A AND NOT B,Y = NOT A AND B

第三步:等价类划分

参数 有效等价类 无效等价类
当前温度 -20 ~ 80 < -20, > 80
目标温度 0 ~ 60 < 0, > 60
工作模式 加热、制冷、自动 其他值(如 0xFF)

第四步:边界值分析

重点关注温差边界:

  • 当前温度 = 目标温度 - 2°C(刚好触发加热)
  • 当前温度 = 目标温度 - 1°C(不触发加热)
  • 当前温度 = 目标温度 - 3°C(应该触发加热)
  • 当前温度 = 目标温度 + 2°C(刚好触发制冷)
  • 当前温度 = 目标温度 + 1°C(不触发制冷)
  • 当前温度 = 目标温度 + 3°C(应该触发制冷)

你看,四种方法配合下来,测试用例既全面又高效。没有冗余,也没有遗漏。

我的建议:刚开始用这些方法时,可能会觉得麻烦。但坚持用 2~3 个项目,你就会发现:测试用例的质量提升了,返工率下降了,定位 bug 的时间也缩短了。这些方法不是束缚,而是工具。