3. 测试用例设计方法:等价类划分、边界值分析、因果图法在嵌入式场景的应用
嵌入式测试,说白了就是跟有限的资源和苛刻的环境打交道。你想想看,一个跑在MCU上的程序,Flash就那么大,RAM更是抠着用,测试用例要是设计得不好,要么测不全,要么测到死机。今天我就把我在几个量产项目里摸爬滚打的经验,跟大伙儿聊聊这三种最实用的用例设计方法。
3.1 等价类划分:别在同一个坑里摔三次
等价类划分的核心思想很简单:把输入域分成若干个子集,每个子集里挑一个代表值来测。为什么?因为同一个等价类里的值,程序处理逻辑是一样的。你测了100个有效值,不如测1个有效值加1个无效值来得实在。
核心原则:每个等价类至少覆盖一次,无效等价类要单独测。
我在一个车载传感器项目中遇到过这么个事:一个温度采集模块,输入范围是 -40°C 到 125°C。测试小哥写了50个用例,从 -40 到 125 每隔几度测一次。我说你停,这50个用例其实只覆盖了1个有效等价类。真正该测的是:
- 有效等价类:-40 到 125 之间的任意值(比如 25°C)
- 无效等价类(低于下限):比如 -50°C
- 无效等价类(高于上限):比如 130°C
你看,3个用例就覆盖了50个用例的测试效果。这就是等价类划分的威力。
我的习惯:先列所有输入条件,再画等价类表。有效等价类一般只有一个,无效等价类可能有多个(空值、超范围、非法格式等)。
3.2 边界值分析:Bug最爱藏在边界上
为什么边界值这么重要?因为程序员写判断条件时,最容易在边界上犯迷糊。比如 if (temp >= -40 && temp <= 125),你写的是 >= 还是 >?差一个等号,天差地别。
边界值分析的基本套路:取每个等价类的边界值,以及边界值两侧的邻值。
| 边界类型 | 取值 | 说明 |
|---|---|---|
| 下边界 | -40 | 刚好在范围内 |
| 下边界-1 | -41 | 刚好低于范围 |
| 上边界 | 125 | 刚好在范围内 |
| 上边界+1 | 126 | 刚好高于范围 |
我曾经在一个步进电机驱动项目里,就因为边界值没测全,导致电机在极限位置抖动。原因是位置计数器用了 uint16_t,最大值 65535,当计数到 65535 时再加 1 就溢出了。边界值分析如果覆盖了 65534、65535、0、1 这几个点,这个问题当场就能发现。
注意:嵌入式里除了数值边界,还有时间边界、缓冲区边界、状态机边界。比如看门狗超时时间、串口FIFO深度、任务调度周期,这些都要做边界值分析。
3.3 因果图法:多条件组合的逻辑迷宫
等价类和边界值主要处理单输入条件。但嵌入式系统里,很多功能是多个条件组合触发的。比如:
- 刹车踩下 && 车速 > 0 → 点亮刹车灯
- 刹车踩下 && 车速 == 0 && 档位在P挡 → 不点亮刹车灯(节能逻辑)
这种多条件组合,用因果图法最合适。因果图的核心是:原因(输入条件)→ 中间节点(逻辑组合)→ 结果(输出动作)。
我建议你按这个步骤来:
- 列出所有原因(输入条件):比如刹车踩下、车速 > 0、档位在P挡
- 列出所有结果(输出动作):比如点亮刹车灯、不点亮刹车灯
- 画出因果图:用与门、或门、非门连接原因和结果
- 转成判定表:把因果图转成真值表,每一列是一个测试用例
- 简化判定表:合并冗余列,减少用例数量
下面是我画的一个简化版因果图,展示刹车灯控制逻辑:
画完因果图,下一步就是转成判定表。判定表的每一列就是一个测试用例。我一般用这个模板:
| 用例编号 | 原因A: 刹车踩下 | 原因B: 车速 > 0 | 原因C: 档位在P挡 | 预期结果 |
|---|---|---|---|---|
| TC-01 | 1 | 1 | 0 | 点亮刹车灯 |
| TC-02 | 1 | 0 | 1 | 不点亮刹车灯 |
| TC-03 | 1 | 0 | 0 | 点亮刹车灯 |
| TC-04 | 0 | 1 | 0 | 不点亮刹车灯 |
| TC-05 | 0 | 0 | 1 | 不点亮刹车灯 |
| TC-06 | 0 | 0 | 0 | 不点亮刹车灯 |
我的经验:因果图法在嵌入式里特别适合测状态机、安全互锁逻辑、多传感器融合决策。但要注意,条件太多时判定表会爆炸(2^n)。我一般控制在4-6个条件,超过这个数就拆成子功能测。
3.4 三种方法的嵌入式实战组合
实际项目中,这三种方法不是孤立的。我一般这样组合:
- 第一步:等价类划分——先把所有输入条件分类,确定有效和无效范围
- 第二步:边界值分析——在每个等价类的边界上补充用例
- 第三步:因果图法——对多条件组合逻辑,画因果图、转判定表
举个例子,一个温控器项目:
- 输入:温度值(-20~80°C)、模式选择(加热/制冷/自动)、定时时间(0~720分钟)
- 输出:继电器通断、LED指示灯、蜂鸣器报警
用等价类划分:温度分有效类(-20~80)和两个无效类(<-20、>80)。模式分三个有效类。时间分有效类(0~720)和两个无效类。
用边界值分析:温度边界取 -21、-20、80、81。时间边界取 -1、0、720、721。
用因果图法:比如“温度 > 75 且 模式为加热 → 蜂鸣器报警并关闭继电器”。这个组合逻辑用因果图一画,判定表一列,测试用例就全了。
我曾经踩过的坑:在一个RTOS任务调度测试里,我只用了等价类和边界值,漏掉了“高优先级任务 && 低优先级任务持有互斥锁”这个组合。结果高优先级任务一直等锁,低优先级任务被抢占,死锁了。这就是因果图没做全的后果。
3.5 嵌入式场景的特殊考量
嵌入式测试用例设计,跟纯软件测试有几个不一样的地方:
| 考量点 | 说明 | 用例设计建议 |
|---|---|---|
| 资源受限 | RAM/Flash有限,用例不能太多 | 优先用边界值和因果图,减少冗余等价类 |
| 实时性 | 响应时间必须在规定窗口内 | 边界值要覆盖时间边界(如中断响应时间) |
| 硬件耦合 | 软件行为依赖硬件状态 | 因果图里要把硬件状态作为原因节点 |
| 状态机 | 系统在不同状态下的行为不同 | 每个状态都要做等价类和边界值分析 |
嗯,说到状态机,我再多提一句。嵌入式系统里到处都是状态机:通信协议状态、设备初始化状态、任务调度状态。每个状态下的输入条件都不一样,所以因果图法在状态机测试里特别好用。我习惯先画状态转换图,再对每个状态单独画因果图。
总结一下:等价类划分帮你减少用例数量,边界值分析帮你抓住最容易出Bug的地方,因果图法帮你理清多条件组合逻辑。三者结合,嵌入式测试用例的质量就能上一个台阶。