第8章:嵌入式系统软件测试

软考视角:这一章是下午案例题的"明星章节"——MC/DC 覆盖率计算、圈复杂度计算、代码审查找错、测试用例表填空、DDP(缺陷探测率)计算,都是反复出现的题型。上午题则爱考测试定义演进、黑盒/白盒方法分类、四种测试级别的组织者。本章必须动手做题,光背概念不够。

本章在讲什么

嵌入式软件有实时性、高可靠性、高安全性等特点,一旦出错可能造成严重后果(刹车失灵、飞控异常),所以嵌入式软件测试比一般软件测试要求更严格、流程更规范。本章围绕"测什么、怎么测、在哪测、谁来测"展开:

  1. 测试的定义和认识演进(8.1);
  2. 测试技术:过程、方法、类型、工具、环境(8.2);
  3. 测试实践:面向对象测试、基于模型测试、分布式测试和三个完整案例(8.3)。

核心概念拆解

1. 软件测试定义的演进(上午题考点)

  • 1973 年 Bill Hetzel:第一个明确定义——"软件测试就是建立一种信心,确信程序能够按期望的设想进行工作"。核心是"证明程序能工作",缺陷在于不可能完全证明软件正确。
  • 1979 年 Myers《软件测试之艺术》"软件测试是为了发现错误而执行软件的过程"。三个重要观点:
  • 测试是为了证明程序有错,而不是证明程序无错;
  • 好的测试用例在于能发现至今未发现的错误
  • 成功的测试是发现了至今未发现的错误的测试。 "查出错误的测试才是成功的测试"——测试是破坏性过程。
  • 认识转变:无错软件功能未必正确;测试对象不仅是程序代码,还包括需求文档、设计文档、用户手册等工作产品(狭义 vs 广义测试)。有资料表明 60% 以上的软件错误不是程序错误,而是需求和设计错误——所以要提倡软件全生命周期测试
  • 验证与确认(V&V)
  • 验证(Verification):检查软件是否正确地实现了规格书所定义的功能和特性——"是否正确地做了事"(输入输出对照规格);
  • 确认(Validation):证实特定目的的功能是否已实现,一切从客户需求出发,主要通过软件评审活动实现——"是否做了正确的事"。
  • 1990 年 IEEE/ANSI:测试是在规定条件下运行系统或构件的过程,观察记录结果并给出评价。
  • 1992 年 RTCA《DO-178B》:测试是执行系统或部件以验证其满足需求并检测错误的过程;软件测试是软件验证的一个组成部分

2. 软件测试发展的五个阶段

  1. 软件调试时期:调试、测试不分家,测试是调试的一部分;
  2. 论证时期(1957 年 C.Baker 区分调试与测试):目的是"证明软件无错",但被实践抛弃;
  3. 破坏性测试时期(1979 Myers):以发现错误为目的,查错才算成功;
  4. 生命周期评估时期:测试与开发同步开始,覆盖需求和设计,出现可靠性测试和可靠性增长模型;
  5. 预防测试时期:测试策划、分析、设计可显著改进需求和设计,强调软件可测试性

嵌入式软件测试也对应经历五个阶段:开发人员简单调试 → 开发人员自测 → 开发人员互测 → 非开发人员测试 → 第三方专业测评机构按流程测试

3. 测试的 10 条重要原则(Myers 原则的展开)

  1. 测试用例必需部分是定义预期输出/结果
  2. 程序员应避免测试自己编写的程序;
  3. 编写软件的组织不应测试自己编写的软件;
  4. 彻底检查每个测试的执行结果
  5. 测试用例既要覆盖有效/预期输入,也要覆盖无效/未预料输入
  6. 检查"未做应该做的"只是测试的一半,另一半是检查"做了不该做的";
  7. 避免测试用例用后即弃(除非一次性软件);
  8. 策划测试时不应默许假定不会发现错误;
  9. 程序某部分存在更多错误的可能性,与该部分已发现错误的数量成正比(错误群集现象);
  10. 软件测试是极富创造性、极具智力挑战性的工作。

4. 测试过程五步(背顺序)

测试需求分析 → 测试策划 → 测试设计和实现 → 测试执行 → 测试总结

  • 测试需求分析:确定测试类型(功能、性能等)及要求,并与测试级别匹配;确定测试项优先级、充分性要求、终止要求、追踪关系。产出测试需求规格说明(或写入测试计划),并评审。
  • 测试策划:确定测试策略、技术方法、受控工作产品清单、资源要求、风险分析、结束条件、评价准则、进度。产出测试计划
  • 测试设计和实现:分解测试项、设计测试用例、确定执行顺序、准备测试数据、建立并校核测试环境、必要时编写驱动模块和桩模块。产出测试说明。测试用例要写清:初始化要求、输入(名称、性质、来源、顺序)、期望结果(应具体,不能笼统)、评估准则、执行步骤、前提和约束、终止条件。
  • 测试执行:如实填写测试记录;按期望结果与评估准则判定用例是否通过;不通过时区分缺陷类型(测试自身的缺陷记录变更,被测软件缺陷记入软件问题报告);全部执行完分析充分性,必要时补充测试。
  • 测试总结:评价测试工作(文档变化、未覆盖范围、未解决事件)和被测软件(差异分析、性能评估);编写测试报告;最后进行测试总结评审。

5. 测试方法体系

静态测试 vs 动态测试

  • 静态测试:不运行程序。对文档用检查单审查;对代码用代码审查、代码走查、静态分析
  • 代码审查:检查代码与设计的一致性、标准执行情况、逻辑正确性、结构合理性、可读性;
  • 代码走查:测试人员小组集体"人脑执行"测试用例,扮演计算机角色;
  • 静态分析:机械性、程序化的分析,含控制流分析、数据流分析、接口分析、表达式分析,通常借助工具。
  • 特点:不必设计可执行的测试用例;发挥人的逻辑思维;容易开展;发现错误的同时定位了错误
  • 动态测试:实际运行程序,按是否了解内部结构分黑盒白盒
  • 黑盒测试:又称功能测试、数据驱动测试、基于需求的测试,只看输入/输出关系;
  • 白盒测试:又称结构测试、逻辑测试、基于程序的测试,依据内部逻辑结构设计用例;
  • 各测试级别的方法搭配(高频考点):配置项测试和系统测试一般用黑盒;部件测试主要黑盒、辅助白盒;单元测试一般白盒、辅助黑盒

黑盒测试方法(8 种)

  1. 功能分解:功能抽象分解为功能单元 + 数据抽象产生测试数据;
  2. 等价类划分:划分有效等价类(合理输入)和无效等价类(不合理输入),每类编号并设计用例;
  3. 边界值分析:用等于、小于、大于边界值的数据测试——满足边界的输入发现计算错误,不满足的发现域错误
  4. 判定表:四部分——条件桩、条件条目、动作桩、动作条目;每一列是一条规则,规则即测试用例;
  5. 因果图:把自然语言的功能说明转成判定表再生成用例;适合输入条件组合的情况;因果图太大时不宜使用;
  6. 随机测试:输入随机选取,预期输出难确定,多用于可靠性测试和系统强度测试
  7. 猜错法:有经验人员凭错误清单写用例;
  8. 正交试验法:因子=操作对象/外部因素,状态=因子取值,用正交表组合,大幅减少用例数。

白盒测试方法(6 类)

  1. 控制流测试(覆盖)——重中之重,六种覆盖由弱到强:
  2. 语句覆盖:每条语句至少执行一次;
  3. 分支覆盖(判定覆盖):每个判定的真、假分支各执行一次;
  4. 条件覆盖:每个判定中每个条件的可能取值至少满足一次;
  5. MC/DC(修订的条件/判定覆盖):每个判定中的每个条件都曾独立影响判定结果至少一次(其他条件不变,只改这一个条件就能改变判定结果)。安全性要求高的软件采用,效率与数量平衡好;
  6. 条件组合覆盖:覆盖判定中条件的所有组合;
  7. 路径覆盖:覆盖所有可能路径(大程序需简化循环次数)。
  8. 注意:画控制流图时,复合条件要拆成一系列单个条件的嵌套判断
  9. 数据流测试:分析变量的定义-引用,查找未定义就使用、定义了未使用的变量;一般用工具;
  10. 程序变异:错误驱动测试,查剩余的小错误;
  11. 程序插桩:向被测程序插入操作(不影响运行过程和功能),数据记录量大,多用工具;
  12. 域测试:判别程序对输入空间的划分是否正确,限制多,特殊要求时用;
  13. 符号求值:变量取"符号值"执行,可验证公式、产生路径测试数据。

6. MC/DC 覆盖率怎么算(下午题高频)

以逻辑与条件 A && B 为例:A、B 有 TT/TF/FT/FF 四种组合,但当 A=FALSE 时,无论 B 为何整个判定都是 FALSE,B 不能独立影响结果,所以只需 TT、TF、FX(X 表示 B 任意) 三种情况 → 最少 3 个用例满足 MC/DC。

书中例 8-2 的两个同构判断(条件相同且第二条件互斥)需要 4 个用例;实例 2 的函数 num_of_passer:100% 语句覆盖最少 2 个用例、100% 分支覆盖最少 2 个、100% MC/DC 最少 4 个(第 4 行组合条件需 4 个,第 7 行需 3 个但可在循环中复用)。实例 3 的单个 && 判断:语句 1、分支 2、MC/DC 3。

做题套路:先数判定和条件 → 写出条件真值组合 → 识别短路逻辑(&& 的前项为假时后项失效;|| 的前项为真时后项失效)→ 数最少用例。

7. 测试类型(按测试内容分,13 种)

逻辑测试、功能测试、性能测试、接口测试、人机交互界面测试、强度测试、余量测试、安全性测试、恢复性测试、边界测试、数据处理测试、安装性测试、容量测试。要点:

  • 性能测试:处理精度、响应时间、数据量、占用空间、并发处理能力等;
  • 强度测试:强制软件在不正常到发生故障的情况下运行(设计极限到超出极限),且需持续规定时间不中断
  • 余量测试:检验是否达到需求的余量,无明确要求时一般至少留有 20% 的余量(处理时间、吞吐能力、存储量);
  • 安全性测试:安全性关键部件必须单独测试安全性需求;要测"0"、穿越"0"及从两个方向趋近"0"的输入;测最坏情况配置下最小/最大输入数据率;
  • 恢复性测试:证实克服硬件故障后系统能正常继续工作且不受损害;
  • 容量测试:检验软件能力的最高程度(最高响应时间、并发数等)。

8. 四个测试级别(按开发阶段分,必背组织者)

级别 对象 目的 组织者 主要方法
单元测试 软件单元 检查单元能否正确实现设计说明的功能/性能/接口 软件供方,可委托第三方 静态+动态,白盒为主
部件测试(集成/组装测试) 组装过程+软件部件 检验单元与部件间的接口关系,验证符合设计 软件供方,测试人员与开发人员相对独立 静态+动态,黑盒为主辅白盒
配置项测试 计算机软件配置项 CSCI 检验配置项与软件需求规格说明的一致性 供方组织、由独立于开发的组织实施 黑盒
系统测试 完整集成的系统 真实环境下检验配置项与系统正确连接,满足任务书要求 软件需方组织、独立于开发的组织实施 黑盒

开发与测试的对应关系(V 模型):需求分析↔配置项测试、概要设计↔部件测试、详细设计↔单元测试、编码/系统分析设计↔系统测试。

部件测试的组装策略: - 一次性组装:全部单元测完再一起集成。优点工作量小,缺点定位错误困难; - 增值式组装(递增集成):边组装边测试,又分自顶向下、自底向上、"三明治"、定向冒险、功能定向等。

各级别测试完成后都形成五件套文档:测试计划、测试说明、测试报告、测试记录、测试问题报告

9. 测试工具(三类)

  • 静态测试工具:复杂度分析、数据流分析、控制流分析、接口分析、句法和语义分析等;
  • 动态测试工具:覆盖分析、捕获和回放、存储器测试、变异测试、仿真器及性能分析等;
  • 测试支持工具:测试计划生成、测试设计与数据生成、用例管理、问题管理、测试配置管理等,支持整个测试过程。

选择工具要考虑:需求及确认、成本和收益分析(总成本含挑选、安装、培训、维护及改变流程的成本)、整体质量因素(易用性、互操作性、稳定性、经济实用性、可维护性)。

10. 嵌入式软件测试的特殊性与测试环境(本章特色)

为什么嵌入式软件测试更难:目标机未开发完成前软件不能真正运行(动态测试没法用);资源有限、接口专用,监测和观察输出困难;输入/输出涉及专用端口和多种信号量(数字量、电压量、电流量、脉冲量、开关量),还有实时时序要求——测试输入和结果获得都很困难

三种测试环境:

  1. 宿主机模拟环境:在宿主机上用模拟技术建立运行环境。两类:
  2. 基于目标机芯片的模拟:模拟 CPU(指令集、寄存器、中断)、内存(寻址、读写)、外围可编程芯片、器件间连接;
  3. 基于交叉编译的模拟:先做硬件依赖性分析,把输入/输出命令用 API 替换,交叉编译成宿主机可执行代码,重点是模拟输入/输出。
  4. 交联式测试环境:逼近真实环境,可接入实物或设备模拟器;目标机硬件、外围接口、输入指令数据全是真实的,与真实系统有一致的映射关系(相同接口、I/O 格式速率、时序)。由目标机、模拟器、控制盒、测试输入及输出分析设备四部分组成。搭建方便且环境真实,是目前使用最多的嵌入式测试环境;配置项和系统测试阶段同时也是软硬件集成测试阶段。
  5. 全实物测试环境:完全置于真实实物环境,系统测试阶段常用。

11. 新开发方法下的测试实践

  • 面向对象软件测试:按 OO 模型分五类——OOA 测试、OOD 测试(对分析设计文本的前期关键性测试)、OOP 测试(编程风格与代码实现)、OO 单元测试(类成员函数,可用等价类、因果图、边界值、逻辑覆盖等传统方法)、OO 集成测试(成员函数间相互作用、类间消息传递)。注意:传统自顶向下/自底向上集成策略在 OO 软件中无意义,需在整个程序编译完成后做基于黑盒的集成测试,策略有基于线程的测试和基于使用的测试。OO 系统测试与传统系统测试内容基本相同。
  • 基于模型的软件测试(MBT):先把需求抽象成机器可读的模型(有限状态机 FSM),内容包括模型程序、Test Harness/Steper/Adapter、策略、测试执行器;每执行一次生成的用例可能不同,能发现很深路径的缺陷。难点在于建模。
  • 基于模型开发软件的测试:模型的正确性决定代码的正确性,验证模型的方法:评审、分析(静态)+ 仿真(动态);只有动态仿真结果才能做覆盖率分析。插值表模型覆盖率类型:条件覆盖、分支(判定)覆盖、MC/DC 覆盖、信号范围覆盖、组合逻辑块覆盖等,工程常用前三种。
  • 分布式软件测试:目的——资源共享、分散操作、集中管理、协同工作、负载均衡、过程监控。特点:网络化、分布性、开放性(可移植、互操作、可伸缩、易获得,可用 COTS 产品)、实时性、动态性、处理不确定性。关键技术:适合集中式的分布式策略(中心机控多台受控机)、基于消息的结点通信、测试任务调度(静态/动态/混合)。

12. 三个测试实例(下午题原型,必须吃透)

实例 1 汽车刹车控制器——考点:接口测试策略 + DDP 计算 + 带约束的状态转换测试。 - 串行输入接口测试策略:测试正常和异常指令的响应;测试内容为读取刹车次数、清除刹车次数两种指令;对"读取刹车次数指令"的鲁棒性测试考虑:帧头错误、指令码错误、帧长错误、帧尾错误、整个指令长度超过 4 字节。 - 带"单次运行进入正常模式后不得再进入维护模式"约束的用例设计:前提条件应改为上电前置 InD1 为高电平(维护模式、红灯)→ 发低电平进入正常模式(绿灯)→ 再发高电平,软件应不响应、灯保持绿色。这样既验证了约束,又排除了设备故障的干扰。 - DDP(缺陷探测率)= 测试发现的问题数 / 软件中总共发现的问题数。本题:(17+31)/(17+31+2) = 48/50 = 96%

实例 2 双余度数据采集软件——考点:圈复杂度 + 代码审查 + 覆盖率。 - 圈复杂度三种算法: 1. 数代码:基数为 1,每个分支(if/for/while/do-while)加 1,switch 每个 case 加 1,复合条件按条件个数加; 2. 流图:V(G) = E − N + 2(边数−结点数+2); 3. 流图:V(G) = P + 1(判定结点数+1)。 本题三种算法都得 7;工程一般要求圈复杂度不大于 10。 - 代码审查找错(对照设计说明逐行读代码): 1. 返回值类型错误:说明要返回 −1,代码却定义 unsigned int,应为 int; 2. 变量 counter 未初始化,应初始化为 0; 3. 边界检查 num > 16 与循环 n <= num 配合导致数组越界(可改 num >= 16n < num,只改一处); 4. 判断条件 > 45 与说明"不小于 45"不一致,应改为 >=。 这体现了代码审查"检查代码和设计的一致性"的核心。

实例 3 飞行器供油阀控制软件——考点:测试方法选择 + 覆盖率计算 + 综合用例表填空。 - 白盒测试基于软件源代码进行;题目给的是功能说明,故最恰当的方法是黑盒测试(灰盒介于两者之间,既看输入输出正确性也通过表象判断内部状态)。 - 单个 && 判断:100% 语句覆盖 1 个用例、100% 分支覆盖 2 个、100% MC/DC 覆盖 3 个。 - 用例表填空要逐条对照 8 条需求说明,注意:油量差 ≥50L 用剩油多的油箱、否则同侧优先;一油箱+一发动机故障由无故障油箱给无故障发动机供油;两油箱或两发动机故障双发断油报特级故障;高低级故障同时发生只上报最高级。填空时还要会"反推":由输出(特级故障+双发断油)反推隐藏的故障输入(如序号 10 右发动机 ER 必故障)。

初学者容易踩的坑

  1. 验证与确认搞混:验证=对规格做("正确地构造了产品",Are we building the product right);确认=对需求/客户做("构造了正确的产品",Are we building the right product)。评审属于确认活动。
  2. 覆盖强度排序记不清:语句 < 分支(判定)< 条件 < 条件组合/路径;MC/DC 强度介于条件与条件组合之间。注意条件覆盖不一定包含分支覆盖——满足每个条件真假各一次,但判定整体可能永远只取一个方向(书中例 8-2 的两个用例就未满足分支覆盖)。
  3. MC/DC 数用例不看短路逻辑:对 A && B 直接写 4 种组合就错了;要利用 &&/|| 的短路特性,一个条件失效时另一条件无法独立影响结果,因此 n 个条件的 &&/|| 判定最少 n+1 个用例。
  4. 测试级别的组织者张冠李戴:单元/部件测试=供方;配置项测试=供方组织但独立于开发的机构实施;系统测试=需方组织。另外配置项测试对需求规格,系统测试对系统/子系统设计文档和任务书。
  5. 复合条件画控制流图不拆分if(a && b) 在控制流图中必须拆成两个嵌套的判定结点,否则圈复杂度和覆盖计算全错。
  6. 圈复杂度漏算复合条件:数代码法中 if(a && b && c) 不是加 1 而是加 3。
  7. 鲁棒性测试只测正常值:接口测试必须同时覆盖正常和异常,异常包括帧头/帧尾/帧长/校验/超长等各种"包坏了"的情况。
  8. DDP 公式记错:分母是"测试发现 + 用户反馈"的总数,不是测试发现数;本题是 48/50 而不是 48/48。
  9. 嵌入式测试环境的作用域混淆:宿主机模拟用于早期脱离目标机测试;交联式环境用于配置项/系统测试(使用最多);全实物用于系统测试阶段。仿真环境下测试必须说明与真实环境的差异并做影响分析。
  10. 传统集成策略套到 OO 软件:OO 集成测试要等全部编译完成、只能做黑盒集成,用基于线程/基于使用的策略——这是与结构化软件最本质的区别之一。

小结 & 备考提示

本章的复习策略是"概念一条线、计算一条线":

  • 概念线(上午题):测试定义演进(Hetzel→Myers→V&V→IEEE/DO-178B)、五阶段发展、10 原则、静态/动态与黑盒/白盒分类、黑盒 8 法与白盒 6 法、13 种测试类型、四个级别及组织者、三类测试工具、三种嵌入式测试环境。
  • 计算线(下午题)
  • MC/DC 最少用例数:单个 &&/|| 判定 n 个条件 → n+1 个;
  • 圈复杂度:V(G)=E−N+2=P+1,复合条件按条件数累加;
  • DDP = 测试发现 / 总发现;
  • 代码审查找错:对照说明查返回类型、初始化、边界(>vs>=、越界)、逻辑方向;
  • 用例表填空:逐条对照需求,注意约束(如模式单向转换)和故障级别取最高。

做题时先读需求说明再动手,答案几乎全部来自原文的"实例化"——这是本章案例题的最大特点。至此第 5~8 章的讲解笔记全部完成,祝备考顺利。