张晓明
(中航西安飞机工业集团股份有限公司,陕西 西安 710089)
随着计算机技术在航空电子系统的不断应用,软件的结构也越来越复杂。在软件整个生存周期中,随着软件开发的进行,发现错误和修正错误的成本将越来越高,因此基于过程的测试尤为重要,而单元测试就是在编码早期阶段发现和修正错误的最好方法,可以提高软件单元的质量[1]。为了验证软件是否满足预期功能,发现并解决软件研制过程中的各种缺陷,优化和进一步提高软件代码质量,仅靠软件测试人员的人工统计分析已不能有效地对被测软件作出准确评价,迫切需要一种专业的软件测试工具。
LDRA Testbed是专业的软件验证与确认的质量控制工具,具有静态分析、数据流分析、动态分析和图形化显示等功能。该工具能够显著降低软件缺陷,提升软件的可靠性和健壮性,已广泛应用于国内各研究所、大学和专业测试机构。
软件单元测试是对软件基本组成单元进行的测试[2]。单元测试的对象是模块。模块的特点有:具有唯一的标识;具有明确的功能;拥有局部数据;实现特定功能的算法;与其他模块或外界存在数据联系;可被其他模块调用。
Testbed支持单个文件、多个文件(Set)和工程直接导入(Makefile)3种方式,可根据实际项目的需要进行选择,一般规模较小的选择前两种方式,规模较大的软件应选择第三种方式。
序列是一组测试用例的集合,在不同的测试级别,如单元和集成测试,还具有隔离全局变量的作用,和其他测试工具如C++Test中的测试套件功能类似。
Testbed支持6种测试级别:
(1)Instrumentation No Coverage:不带覆盖率分析的集成测试;
(2)Code Coverage Only:带覆盖率分析的集成测试;
(3)Module Testing-Isolate Procedures &Generate Code Coverage(Light Grey Box):带覆盖率分析的部件测试;
(4)Module Testing-Isolate Procedures(Dark Grey Box):不带覆盖率分析的部件测试;
(5)Unit Testing-Isolate Procedures &Generate Code Coverage(Pale Grey Box):带覆盖率分析的单元测试;
(6)Unit Testing-Isolate Procedures(Strong Grey Box):不带覆盖率分析的单元测试。
Testbed在设置测试级别的时候,主要是设定打桩范围,如单元测试可以对函数内其他调用进行打桩,序列中的测试用例单独编译,不共享全局变量;部件测试则针对该类或该文件之外的函数进行打桩,序列中的测试用例是共享全局变量的;系统测试则可以针对序列或工程源文件之外的任意调用进行打桩,序列中测试用例是共享全局变量的。
在运行测试用例之前,必须配置编译命令和链接命令。编译命令应指定编译器,并用“-I”选项指定依赖的头文件,如要使用C++11标准,编译命令应附加“-std=gnu++11”[3];链接命令应使用“-L”选项指定依赖的库文件。
Testbed在执行测试用例时,根据具体情况需要添加打桩代码,如该单元使用了一个类的对象,那就需要对类的构造函数和析构函数打桩,否则会直接报错。针对多个测试用例共享的桩函数,则需要进行全局打桩。在全局打桩函数中,针对多个测试用例的不同实现,可以利用Testbed的内置变量ldra_qq_last_test_case、ldra_qq_test_case_number等进行判断区分。
系统函数一般不用打桩,直接调用即可,如果确有需求,也可以进行打桩,打桩方法与普通函数完全一样,可以自己定制系统函数功能,并可以标记为系统桩函数(Testbed会生成函数别名),防止调用混乱出错。
Tesdbed支持语句覆盖(Statement Coverage)、分支/判定覆盖(Branch/Decision Coverage)、分支条件覆盖(Branch Condition Coverage)、条件组合覆盖(Branch Condition Combination Coverage)、修改的条件判定覆盖(MC/DC Coverage)、线性序列代码及跳转覆盖(LCSAJ Coverage)以及DO-178B/C、ISO 26262等标准[4]。值得一提的LCSAJ覆盖是LDRA特有的覆盖分析,是对形成的线性代码(start和finish配对)进行覆盖统计,消除了路径覆盖测试用例成几何倍数,仅需N+1(N为条件个数)个测试用例即可达到100%覆盖。覆盖率配置可以在Testbed中进行定义,如图1所示。
图1 覆盖率配置
创建测试用例后,Testbed会自动识别出需要赋值的变量,并设置预期的返回值(false或true)。测试用例执行符合预期会报告PASS,否则会报告FAIL,如报告FAIL说明函数的设计存在错误,需要进行修改完善。Testbed在强灰盒模式是不分析覆盖率的,因此如需要查看覆盖率,单元测试时必须选择轻灰盒模式。
某项目开发的MIL-STD-1553B总线仿真监控软件能够实时监控航电系统中的1553B总线数据,针对故障和异常状态进行报警,并能够对数据进行记录和回放。该项目的软件开发环境为:操作系统为Windows7;开发工具Qt5.8;开发语言为C++。本文以该项目中C1553.cpp中C1553:setFilter函数为例进行单元测试分析。
本实例完整的编译命令为:“g++"$(Name)"-o "$(Exe)"-I $(SourcedirQuoteUnEnv) $(IncludedirsLeadQuoteUnEnv)-Idebug-ID:QtQt5.8.0·.8mingw53_32include-ID:QtQt5.8.05.8mingw53_32includeQtCore-ID:QtQt5.8.05.8mingw53_32includeQtGui-ID:QtQt5.8.05.8mingw53_32includeQtWidgets-std=gnu++11”。注意由于该项目使用了Qt5.8,采用了C++11标准,编译选项必须带有“-std=gnu++11”。
本实例完整的链接命令:“g++*.o-o "$(Exe)"-lmingw32-LD:QtQt5.8.05.8mingw53_32lib-lshell32-LD:projectDiagnosis1553-lbusapi32 d D:QtQt5.8.05.8mingw53_32liblibQt5Widgetsd.a D:QtQt5.8.05.8mingw53_32liblibQt5Guid.a D:QtQt5.8.05.8mingw53_32liblibQt5Cored.a”。
因为是针对类的函数测试,必然要调用类的构造和析构函数,又因为是单元测试,所以必须对C1553::C1553()构造函数和C1553::~C1553()析构函数这两个函数打桩。
创建第一个测试用例,Testbed自动会识别出bus、rt、sa、bFilter等变量,分别赋值为0、0、0、1,m_mapFilter为QMap对象,它的赋值方式为:{},设置预期返回值为false。测试用例执行显示PASS;创建第二个测试用例,其他赋值不变,设置预期返回值为true,测试用例执行显示FAIL,如图2—3所示。
图2 测试用例设计
图3 测试用例执行
编译运行后,覆盖率分析如图4所示。覆盖率分两类,一个是本次测试用例的覆盖,一个是累计的覆盖统计。由图4可以看到累计的语句覆盖为68%,分支/判定覆盖为50%,LCSAJ覆盖为75%,需要继续设计测试用例提升覆盖率。
图4 覆盖率分析
本文以LCSAJ为例,介绍提高LCSAJ覆盖率的方法。查看LCSAJ报告,共有4个LCSAJ,可以看到第3个LCSAJ没有执行,覆盖率为75%也和上面的统计符合,如图5所示。右键点击查看覆盖率流图分析,其中实线是没有执行的路径,双击第3个LCSAJ,如图6所示。
图5 LCSAJ覆盖率报告
图6 LCSAJ流图分析
可以看到,Testbed已经智能提示用户应该设置条件“itor !=m_mapFilter.end()”,即可满足LCSAJ覆盖。但由于m_mapFilter为QMap
图7 累计覆盖率分析
回归测试指对软件待测版本中相对上一个版本不变的特性进行验证[5],主要用于代码发生更改,通过修改、复用原来的测试用例继续对其进行验证,并且要求保证用例返回PASS。
Testbed中测试用例可以保存为TCF格式,导入原来的测试用例后,校验测试用例是否仍然有效,测试用例图标会显示绿色或红色,绿色表明用例有效,即代码更改后接口、类型等没有改变;红色表示用例失效,接口、类型等已发生变化。用例符合性报告如图8所示。
图8 用例校验报告
针对已发生变化的测试用例,必须逐个修改测试用例的输入、输出以及返回值等,直到再次校验测试用例全部通过。再次编译执行,直到全部用例返回PASS即可通过回归测试。本例中修改完测试用例后,生成的回归报告如图9所示。
图9 回归报告
单元测试是白盒测试的主要手段。通过对软件进行有效的单元测试,可以帮助软件开发及测试人员查看程序质量结果。单元测试的目标不是证明程序完全正确,而是尽可能多地发现软件中的缺陷,提高软件的可靠性和健壮性。本文基于LDRA Testbed详细描述软件单元测试的方法,并给出了具体实例。结果表明Testbed是一个功能强大且方便使用的软件测试工具,可以节省资源、提高产品质量、缩短开发时间,提供调整测试方案和优化软件测试的必要信息。