一种“白黑白”盒混合型航天器软件单元测试技术
2019-01-22郑小萌滕俊元
郑小萌,高 猛,滕俊元
0 引 言
软件单元测试是软件开发过程中重要的质量保证活动,单元测试的质量将很大程度上影响软件产品的最终质量.
目前随着航天器复杂程度和指标要求的不断提高,航天器软件的质量也受到越来越广泛的重视.尤其对于航天型号来说,软件的可靠性、安全性设计尤为重要,正确的单元测试方法和有效的单元测试工具成为保证单元测试质量与效率的关键因素.但由于航天器控制软件是非常复杂的实时嵌入式软件,存在工作模式多,与硬件结合密切,单元测试工作量大、测试难度高等特点,仍需研究有效的测试方法,设计高质量的测试用例,来提高单元测试阶段发现错误的能力[1-2].
本文通过分析总结航天器软件单元测试的常见典型问题,结合航天器软件单元测试的具体实践,提出了一种“白-黑-白”盒混合的单元测试方法,该方法有效解决了现存的单元测试实施难题,同时提高了单元测试的质量与效率,保证了航天器软件的可靠性.
1 单元测试概述
1.1 单元测试内容
单元测试的目的在于发现程序单元模块内可能存在的各种错误.单元测试的内容主要是对模块的5个基本特性进行评价,图1对单元测试进行了概要描述.
图1 单元测试示意图Fig.1 Diagram of unit test
模块接口:在其他测试开始之前,首先要对通过模块接口的数据进行测试.若数据不能正确地输入/输出,则其他测试都是没有意义的[3-4].
局部数据结构:检查局部数据结构以确保临时存储的数据在程序执行过程中完整、正确.
重要的执行路径:执行控制结构中的所有独立路径(基本路径)以确保模块中的所有语句至少执行一次.此时设计测试用例是为了发现因错误计算、不正确的比较或不适当的控制流而引起的错误.此时,基本路径测试和循环测试是最常用、最有效的测试技术.
错误处理:一个好的设计应能预见各种出错条件,并预设各种出错处理通路.
边界测试:边界测试是单元测试的最后一步,也是最重要的一项任务.众所周知软件通常容易在边界上失效,因而采用边界值分析技术,针对边界值及其左、右值设计测试用例,很有可能发现新的错误[5].
综上所述,单元测试期间常发现的错误可能出现在以上各个方面,收集分析、归纳总结相关问题数据对单元测试过程可起到有针对性的指导作用,也可为软件质量问题的举一反三提供参考和依据.
1.2 单元测试常见典型问题
通过对航天型号软件单元测试问题进行梳理与分析,总结出以下单元测试典型多发问题类型,同时结合软件实例给出了相关纠正措施,用于指导后续单元测试方法的选择以及测试用例的设计,如表1所示.
表1 单元测试常见典型问题及实例Tab.1 Common typical problems and instance in unit test
通过分析典型多发问题的本质及原因,可以得出结论,研究高效的单元测试方法、设计完整且充分的测试用例对于有效规避和解决软件单元测试问题可以起到显著作用,同时结合高可信度测试工具,不但能够降低型号软件的研制成本和周期,还可以提高测试效率[6-7],本文将着重介绍一种混合单元测试方法以及该方法中使用的自动化测试工具,并举例说明了混合测试方法对于常见典型问题逻辑分支不可达、缺少边界最大最小值覆盖等情况的解决.
1.3 国内外研究现状
目前在航天领域,航天器软件测试的标准规范主要参考有GJB/Z 141-2004军用软件测试指南、QJA300-2016航天软件测试规范、QJ 3027-98航天型号软件测试规范等文件.针对航天软件单元测试的过程,要求根据任务书、项目开发计划、软件设计文档等具体内容,选择适当的测试方法对软件单元的功能、性能(精度、时间、容量等)、错误处理、边界条件、内存使用情况等方面进行测试,验证软件单元是否正确地实现设计文档中的功能、性能和其它设计约束等要求,并发现软件单元的各种缺陷.
航天软件单元测试一般应符合以下技术要求:
(1) 对软件设计文档规定的软件单元的功能、性能、接口等应逐项进行测试;
(2) 每个软件特性应至少被一个正常测试用例和一个被认可的异常测试用例覆盖;
(3) 测试用例的输入应至少包括有效等价类值、无效等价类值和边界数据值;
(4) 在对软件单元进行动态测试之前,一般应对软件单元的源代码进行静态测试;
(5) 语句覆盖率达到100%;
(6) 分支覆盖率达到100%;
(7) 修正判定/条件覆盖率达到100%;
(8) 对输出数据及其格式进行测试.
目前单元测试一般采用白盒测试方法或者黑盒测试方法[8].黑盒测试用例设计是根据程序的功能进行的,常用方法有等价类划分、边界值分析、判定表、因果图等[9].此时在程序接口处进行测试,检查程序在接收输入后是否能产生符合要求的输出.然而程序的功能定义存在一些人为和主观的因素,常常是不全面的;各个输入数据和操作次序之间也有很多种组合关系,有些组合可能会产生问题,无法保证这些组合都经过了测试.很显然,普通的黑盒测试存在着局限性,很难衡量黑盒测试的完整性.因此引入白盒测试中的逻辑覆盖指标来标识测试结果.
白盒测试针对程序的逻辑结构设计测试用例,用逻辑覆盖率来衡量测试的完整性.逻辑覆盖测试方法包括:语句覆盖、判定覆盖、条件覆盖、判定/条件覆盖、组合覆盖、路径覆盖.这几种覆盖对测试覆盖的强度依次增强.然而白盒测试依赖于程序的控制结构,若其本身有误则无法发现错误.大部分结构化测试策略没有提供一个在路径域中选择测试数据的原则[10-11].
与此同时,国内外有不少研究涉及单元测试领域,提出了大量的测试方法和测试理论.Paul C.Jorgensen在《Software Testing A Craftsman’s Approach》中就提出了软件功能性测试和结构性测试的方法;文献[12]中提出了软件单元测试通常覆盖的区域,执行单元测试应该遵循的基本方法等.这些研究对软件单元测试工作提供了大量的理论参考依据,但研究往往立足于理论本身,缺乏工程操作中切实可行的解决方案.另外,从软件组织对单元测试的实施现状来看,许多软件开发组织存在以下问题:
(1) 存在工作量大、测试人员设计测试用例数量多、测试周期长等问题,导致在软件开发各个周期进行高效的单元测试仍存在难度,急需提供有效的自动化测试手段;
(2) 现存的大多数自动化测试工具,由于针对用例设计相关功能效率较低,导致整体单元测试效率不高.
针对这些问题,本文介绍的“白-黑-白”盒混合单元测试方法中将使用一种自动化测试工具SunwiseAUnit,其基于代码生成测试用例的特性可以很好地适用于航天软件的回归测试阶段.利用工具自身简单易用、高自动化的特点,使得测试人员可在大规模的回归测试中进行快速有效的自动化单元测试,同时自动生成的用例也可供测试人员快速校验、复查已有手动设计的测试用例, 大幅度提高单元测试的效率.
2 基于“白-黑-白”盒的混合单元测试方法
如上节所述,白盒与黑盒测试方法具有各自的优缺点,为充分利用两种测试技术的优势,目前已形成一种介于黑盒和白盒之间的灰盒测试方法,考虑到在安全关键软件测试中,单元测试的覆盖准则往往是双百(语句、分支)甚至是三百(语句、分支、修正判定/条件覆盖),本文在灰盒测试方法基础之上提出了一种白-黑-白盒的混合单元测试方法(如图2),该方法自动生成大量有效测试用例可以使得语句、分支、修正判定/条件覆盖性(MC/DC)得到全面、有效的保障.
图2 “白-黑-白”盒混合单元测试方法Fig.2 The white-black-white-box unit test method
2.1 基于自动化测试工具的白盒测试
步骤一:进行白盒测试.在设计测试用例之前,首先关注模块内部的逻辑分支和路径的执行情况,即考虑以白盒测试中的逻辑覆盖技术为基础,选择出需要执行的分支或路径.该步骤利用自动单元测试工具SunwiseAUnit自动生成测试用例,主要优势体现如下:
(1) 基于工具提供的表格驱动可视化设计视图,利用代码分析技术自动获取函数的接口、全局变量、底层调用等信息.用例编辑器提供用例输入、预期输出、运行结构、桩函数等,提高了用例设计易用性.
(2) 基于最前沿的动态符号执行技术与约束求解技术,对位运算、浮点运算、全局变量、端口访问、函数桩、函数指针等进行特殊优化并自动生成测试用例.测试用例自动生成功能的实现主要依靠单元测试用例生成引擎(核心如图3所示),以前端处理器产生的中间表示为输入,采用混合符号执行技术、约束求解技术来产生满足覆盖准则的测试输入,经过若干次迭代,最终生成测试输入集.本部分除了包括C源码级解释器、符号执行器、约束收集器、约束求解器等之外,重点提出了高效和高覆盖率的路径搜索及选择策略.用例自动生成包含的3个主要部分:
图3 用例生成引擎Fig.3 Test case automatic generation engine
1) 前端处理器(FrontEnd):给定源代码和编译配置,对被测软件代码进行预处理、词法/语法分析、CFG构建和符号表构建,产生后续分析所需要的各种中间表示IR.
2) 混合内存模型(MemModel):混合内存模型是解释执行和符号执行的基础,提供对C语言各类语言结构、类型、运算的内存模型支持;具体地,又分为具体内存模型和抽象内存模型.
3) 单元测试用例生成引擎(Core):该部分是SunwiseAUnit的核心部分之一,本部分具体包括:
(a)C源码级解释器(CInt):对给定中间表示进行具体解释执行,根据控制流图对具体内存模型进行存取,并完成函数调用、条件跳转等控制流的执行;
(b)符号执行器(SymEx):满足真实嵌入式C程序,与源码级解释器同步执行,在具体执行过程中,维护抽象内存模型的状态;
(c)约束收集器(ConsGen):在符号执行引导下,收集一条路径的约束公式;
(d)约束求解器(Solver):对收集的布尔约束进行求解;
(e)高效和高覆盖率的路径搜索及选择策略(PathSel):根据生成的输入以及历史数据,采用高效的搜索算法,确定下一个要执行的路径,并指导解释器迭代执行.SunwiseAUnit工具采用当前最新的理论成果,并根据大量真实案例进行了算法调优.
(3) 利用工具自身提供的覆盖率监控技术(满足DO-178B的覆盖率测量功能),对代码进行插桩可以获得语句、分支、MC/DC覆盖率信息.
此外,很值得一提的是,工具自动静默地生成测试执行需要的脚本和测试驱动程序这项工作,大大减少了开展测试所需的额外工作量,可让测试人员专注于用例设计,很好的解决了测试驱动难以编写的难题.与此同时,工具无需依赖复杂编译环境和设置,仅使用编译好的被测软件就可以进行单元测试.该模式下测试用例数据自动转换为目标虚拟机的驱动脚本,通过这些脚本完成测试驱动、测试用例的输入(包括桩)设置以及预期结果比较(过程如图4所示),大大节省了测试过程中由于频繁编译浪费的时间成本.
图4 无需编译的测试用例执行过程Fig.4 Execution of test cases without compile
2.2 基于边界值分析法的黑盒测试
步骤二:进行基于边界值分析法的黑盒测试.在上一步骤中已导出测试用例集合,保证了程序中的每一条语句和分支都至少执行一次.本步骤从功能测试的角度,选择边界值分析法补充用例.经验表明输入域的边界比中间更加容易发生错误,为得到合理而严格的测试用例集,选择该方法简单而有效.按照边界值分析的方法,应该选取刚好等于、稍小于和稍大于等价类边界的数据作为测试数据.
2.3 基于逻辑覆盖检查的白盒测试
步骤三:进行白盒测试.除了步骤二中使用边界值对用例进行补充,还应针对步骤一中最后获取的覆盖率信息所提示的没有达到要求的情况,使用逻辑覆盖进行有针对性的测试用例设计.考虑逻辑覆盖方法中由弱至强的递进关系,推荐使用路径覆盖法这种最强的逻辑覆盖方法,达到最终覆盖率100%的目的.
综上所述,利用“白-黑-白”盒的测试方法能达到单元测试要求的完整性,又能避免不必要的重复.同时利用自动化测试工具多项特点可以有效解决1.3节中提出的单元测试现存难题.这种测试方式比单一的黑盒或白盒方法更有效、更完整,从而使得整个单元测试过程达到既充分、高质量,又最小花费的效果.
3 航天器软件单元测试的典型应用
某软件中待测函数为ptrToUserDefObserveTest1,其源码如图5所示.待测函数有两个参数p和q,其中q为指向int的指针类型.该函数的功能是:若整型参数p在0~20之间时,指针q指向的值为p的2倍,上限值为30,否则指针q指向的值为-1.
图5 待测函数源码Fig.5 Function source code
基于“白-黑-白”混合测试方法实施步骤为:
1) 基于自动化单元测试工具,自动生成表2中的用例1与用例2,执行用例获得语句、分支及MC/DC覆盖率信息.如图6所示,语句覆盖率4/7、分支覆盖率1/4,即执行了第一个True分支语句.
2) 基于边界值分析法,补充设计测试用例3与用例5,选取刚好等于边界值的数据进行测试.
3) 基于逻辑覆盖检查,对未达到覆盖率要求的语句或分支补充设计测试用例4.
表2 基于路径覆盖法的用例设计Tab.2 Test cases based on path coverage method
图6 自动化工具函数运行结果Fig.6 Function running results of automation tool
对于更为复杂的程序,在步骤1中可同理创建剩余分支用例,复制用例给出输入值与预期值即可,直至达到测试标准,语句覆盖率、分支覆盖率、MC/DC覆盖率均为100%(如图7所示).
图7 用例执行结果三项覆盖率达100%Fig.7 Test case coverage rate is 100%
4 结 论
目前在软件测试技术研究领域,单元测试相关工作已经得到了足够重视,但实践中常常由于开发周期、项目进度、经费等原因,导致单元测试不充分.因此在实际工作中深入研究和应用有效的软件单元测试方法,对于保证和提高航天器软件的可靠性与安全性具有重要的现实意义.本文主要针对航天器软件的单元测试方法进行了一些探索,在实践中总结出的“白-黑-白”盒的混合单元测试方法及测试用例自动生成技术,可有效提高单元测试效率,同时对提高单元测试的质量也具有现实指导作用.