测试策略制定基础:基于风险的测试策略、基于架构的测试策略

各位做嵌入式集成的同行,咱们今天聊点实在的。测试策略这东西,说白了就是回答三个问题:测什么、怎么测、测多少。我见过太多团队一上来就铺用例,结果关键路径漏了,小问题倒是抓了一堆。嗯,今天咱们就把这个「策略」二字掰开揉碎。

为什么需要测试策略?

你想想看,一个嵌入式系统,从 bootloader 到应用层,从裸机驱动到 RTOS 任务调度,代码量少则几万行,多则上百万。全量回归?时间不允许。随机抽测?风险兜不住。这时候就需要一个策略,帮我们把有限的测试资源,投到最可能出问题的地方。

我个人习惯把测试策略分成两个维度来看:风险维度和架构维度。前者回答「哪里容易崩」,后者回答「怎么分层测」。两者结合,才能形成一张完整的测试地图。

核心观点:测试策略不是「测什么」的清单,而是「为什么测这个、不测那个」的决策逻辑。

基于风险的测试策略(Risk-Based Testing)

我在项目中遇到过这么一件事:一个车载通信模块,团队花了大量时间测 CAN 总线的高频数据吞吐,结果量产之后,真正出问题的却是休眠唤醒时的电平毛刺。为什么?因为大家觉得「休眠唤醒很简单」,没把它当高风险项。

基于风险的测试策略,核心就是四个字:抓大放小。怎么抓?我一般按三个维度来评估风险等级:

风险维度 评估因素 典型高风险的例子
故障影响度 出问题后对系统的影响范围 安全功能失效、数据损坏、系统死锁
故障发生概率 历史缺陷密度、代码复杂度 新引入的驱动、重构过的模块
可检测性 问题是否容易被测试发现 时序竞争、内存泄漏、中断丢失

有了这三个维度,就可以给每个模块或接口打一个风险分。我常用的公式很简单:

风险等级 = 影响度 × 发生概率 × (1 / 可检测性)

注意这个「可检测性」是倒数——越难测出来的问题,风险等级越高。举个例子,一个内存越界问题,影响度是 5(可能导致系统崩溃),发生概率是 3(代码刚重构过),可检测性是 2(跑正常流程很难触发),那风险分就是 5×3×(1/2) = 7.5。超过 6 的,我建议直接划入「重点测试区」。

我的经验:风险评分不用太精确,分三档就够了——高(必须测)、中(抽样测)、低(冒烟测)。搞得太细,反而没法落地。

基于架构的测试策略(Architecture-Based Testing)

光看风险还不够。你想想看,一个嵌入式系统有硬件层、驱动层、OS 层、中间件层、应用层,每层的测试手段完全不同。基于架构的测试策略,就是按照系统的层次结构,逐层制定测试方案。

我习惯把嵌入式软件架构分成四层来考虑:

  1. 硬件抽象层(HAL)——寄存器读写、中断向量、DMA 配置
  2. 驱动层——外设接口、时序控制、数据收发
  3. 中间件/协议栈层——通信协议、文件系统、任务调度
  4. 应用层——业务逻辑、状态机、用户交互

每一层的测试策略侧重点完全不同。我整理了一个对照表:

架构层 测试重点 常用手段 典型风险
硬件抽象层 寄存器映射、位操作、中断响应 单元测试 + 硬件在环 芯片 errata、编译器优化
驱动层 时序、超时、异常恢复 接口测试 + 故障注入 竞态条件、DMA 冲突
中间件层 协议一致性、缓冲区管理 协议测试 + 压力测试 内存泄漏、死锁
应用层 状态转换、边界条件 集成测试 + 场景测试 逻辑缺陷、资源耗尽

这里有个坑,我曾经踩过——跨层接口的测试。比如驱动层和中间件层之间的回调函数,单测各自都过了,联起来就崩。为什么?因为两层的时序假设不一致。所以基于架构的策略,一定要在层与层之间设置「接口集成测试点」。

避坑指南:我曾经在一个项目中,驱动层和应用层各自测了 95% 的覆盖率,结果集成时发现驱动返回的错误码和应用层预期的枚举值对不上。原因?两边的头文件版本不一致。所以,接口测试一定要用同一份头文件编译。

两种策略怎么结合?

你可能会问:风险策略和架构策略,到底用哪个?我的答案是:两个都要,但分主次。

具体做法是这样的:先用架构策略把系统切成若干「测试单元」,每个单元对应一个架构层或一个模块。然后对每个测试单元做风险评分。高风险单元,用更细的粒度去测;低风险单元,用冒烟测试带过。

下面这张图是我常用的策略制定流程:

基于风险与架构的测试策略制定流程 1. 系统架构分解 2. 识别测试单元(模块/接口) 3. 每个单元风险评分 风险等级判定 高风险:全覆盖测试 边界+压力+故障注入 中风险:关键路径测试 正常流程+异常场景 低风险:冒烟测试 基本功能+启动/停止 风险评分 = 影响度 × 发生概率 × (1 / 可检测性)

你看,这个流程的核心就是:架构分解是骨架,风险评估是肌肉。没有骨架,风险评分就找不到落脚点;没有肌肉,架构分解就变成了纸上谈兵。

一个实战例子

拿我之前做的一个智能传感器项目来说。系统架构分了三层:传感器驱动层、数据处理层、通信协议层。按风险评分:

  • 传感器驱动层——影响度 4(数据不准会影响所有上层),发生概率 2(驱动芯片成熟),可检测性 3(容易通过示波器验证)。风险分 = 4×2×(1/3) ≈ 2.7 → 低风险,只做冒烟测试。
  • 数据处理层——影响度 5(滤波算法出错直接导致误报),发生概率 4(算法刚改过),可检测性 2(边界条件难触发)。风险分 = 5×4×(1/2) = 10 → 高风险,做了全覆盖测试,包括浮点精度、数据溢出、中断打断等场景。
  • 通信协议层——影响度 3(丢包可以重传),发生概率 3(协议栈成熟),可检测性 4(有抓包工具)。风险分 = 3×3×(1/4) = 2.25 → 低风险,只测了基本收发和超时重传。

结果呢?量产之后,传感器驱动层确实没出问题,数据处理层抓到了两个边界 bug,通信协议层有一次因为缓冲区配置不对导致丢包——但因为是低风险,我们只做了抽样测试,这个问题在产线烧录时被发现了,影响不大。整体来看,这个策略是合理的。

一个小技巧:风险评分不是一次性的。每次迭代或每个 sprint 结束后,我都会重新审视风险评分。代码改得越多的地方,风险等级就要往上调。

总结一下

基于风险的测试策略,帮你回答「测多少」;基于架构的测试策略,帮你回答「怎么测」。两者结合,就是一张完整的测试地图。我个人习惯是先画架构图,再标风险等级,最后定测试方案。顺序别搞反了——先定风险再分层,容易漏掉跨层接口的问题。