4、测试用例设计方法:等价类划分、边界值分析、判定表法、因果图法、场景法在嵌入式中的应用
说实话,很多刚入行的工程师觉得测试用例设计就是「随便点点,看跑不跑得通」。但在医疗嵌入式领域,这种想法很危险。我见过太多因为测试不充分导致的现场故障,轻则重启,重则...嗯,咱们还是聊技术吧。
这一章,我把自己十几年积累的测试用例设计经验拆开来讲。五种方法,每一种我都会结合嵌入式场景,说说它们到底怎么用、用在哪儿、有什么坑。
4.1 等价类划分:别把时间浪费在重复测试上
等价类划分,说白了就是把输入数据分成几组。同一组里的数据,程序处理逻辑是一样的。你测一个就够了,没必要全测。
举个例子:一个血压计的压力传感器,输入范围是0~300 mmHg。你想想看,测0、测150、测300,这三个值就能代表所有情况了吗?
其实可以。因为程序对0~300之间的值,处理逻辑完全一样。这就是一个有效等价类。那无效的呢?负数和大于300的值,各算一个无效等价类。
我个人习惯,在划分等价类之前,先画一张输入输出关系表。把每个输入参数的可能取值列出来,然后分组。这样不容易漏。
避坑指南:我曾经在一个心电监护项目里,只划分了数值等价类,忘了考虑采样率这个参数。结果程序在50Hz采样率下跑得好好的,换到200Hz就崩了。从那以后,我每个输入参数都会单独做等价类划分。
4.2 边界值分析:嵌入式系统的命门
边界值分析,这是嵌入式测试里我最看重的方法。为什么?因为嵌入式系统里到处都是边界:定时器溢出、缓冲区满、队列空、ADC满量程...
你想想看,程序在边界处最容易出问题。比如一个环形缓冲区,写指针追上读指针时,是覆盖还是阻塞?这个判断逻辑,十有八九在边界处有bug。
具体做法:取每个等价类的边界值,以及边界值两侧的值。比如一个参数范围是1~100,那你要测:0、1、2、99、100、101。一共6个值。
| 边界类型 | 测试值 | 嵌入式场景举例 |
|---|---|---|
| 下边界-1 | 0 | 定时器计数值为0,是否立即触发中断? |
| 下边界 | 1 | 队列中只有1个元素,出队后队列为空 |
| 下边界+1 | 2 | 缓冲区有2个字节,刚好凑成一个数据包 |
| 上边界-1 | 99 | 接近满量程,但还没到 |
| 上边界 | 100 | 满量程,ADC是否饱和? |
| 上边界+1 | 101 | 超量程,程序如何处理? |
4.3 判定表法:理清复杂逻辑的利器
判定表法,适合处理多个条件组合的场景。比如一个输液泵,有「流速设置」「管路状态」「气泡检测」「压力报警」四个条件,每个条件有2~3种取值。组合起来可能有几十种情况。
你靠脑子想?想不过来的。画一张判定表,一目了然。
判定表的结构:
- 条件桩:列出所有输入条件
- 动作桩:列出所有可能的输出动作
- 条件项:每个条件的具体取值(Y/N)
- 动作项:对应每个组合,应该执行的动作
我建议,在嵌入式项目中,判定表不要超过4个条件。超过4个,组合数太多,判定表会变得很庞大。这时候可以考虑用因果图法来简化。
4.4 因果图法:从原因推到结果
因果图法,说白了就是先画图,再转成判定表。它比判定表法多了一步:明确原因和结果之间的逻辑关系。
基本符号:
- 恒等:原因出现,结果就出现
- 非:原因出现,结果不出现
- 与:多个原因同时出现,结果才出现
- 或:任意一个原因出现,结果就出现
举个例子:一个除颤仪的能量释放逻辑。原因有三个:充电完成、电极接触良好、操作员按下放电按钮。结果只有一个:释放能量。
这个逻辑是「与」关系:三个原因同时满足,才能释放能量。画成因果图,就是一个三输入的与门。
为什么会用到因果图?因为有些逻辑关系不是简单的组合,而是有依赖的。比如「充电完成」和「电极接触良好」之间可能有互斥关系。因果图能把这些关系清晰地表达出来。
4.5 场景法:模拟真实使用流程
场景法,是我在嵌入式测试中最喜欢用的方法。为什么?因为它最贴近真实使用情况。
你想想看,用户不会按照你的测试用例来操作设备。他们会乱按、会同时按多个键、会在设备启动时拔电源...这些「异常场景」,等价类划分和边界值分析覆盖不到。
场景法的步骤:
- 确定基本流:用户正常操作,设备正常响应
- 确定备选流:用户操作有变化,但仍在合理范围内
- 确定异常流:用户操作错误,或设备出现故障
- 组合场景:基本流 + 备选流 + 异常流,组合成完整的测试场景
举个例子,一个血糖仪的基本流是:插入试纸 → 滴血 → 等待测量 → 显示结果。备选流可能是:插入试纸后,用户又拔出来重新插。异常流可能是:滴血量不足,设备报错。
我建议,每个功能模块至少设计3~5个场景。一个基本流,两个备选流,两个异常流。这样基本能覆盖90%的使用情况。
4.6 五种方法的组合使用策略
在实际项目中,这五种方法不是孤立使用的。我一般这样组合:
| 测试阶段 | 推荐方法 | 说明 |
|---|---|---|
| 单元测试 | 等价类划分 + 边界值分析 | 针对单个函数或模块,覆盖输入输出 |
| 集成测试 | 判定表法 + 因果图法 | 针对模块间的交互逻辑 |
| 系统测试 | 场景法 | 模拟真实使用流程 |
| 回归测试 | 边界值分析 + 场景法 | 重点覆盖修改过的边界和场景 |
我个人习惯,在写测试用例之前,先画一张「测试策略矩阵」。横轴是功能模块,纵轴是测试方法。每个格子填上测试优先级(高/中/低)。这样能确保每个功能模块都被合适的测试方法覆盖到。
嗯,这一章的内容就到这里。五种方法,每一种都有它的适用场景。关键是要根据你的项目特点,灵活组合使用。记住,测试不是为了证明程序没有bug,而是为了发现bug。方法用得对,bug跑不掉。