刘 博,汪宇昕
(1 北京纵横机电技术开发公司, 北京 100094; 2 北京师范大学附属实验中学, 北京 100032)
轨道交通机车车辆软件大多是嵌入式软件,在行业竞争日趋激烈背景下,要求产品不断压缩上市时间,才能抢占市场获得主动权。然而,在硬件发展日益稳定,而软件问题却逐渐突出的形势下,软件的交付周期和质量问题开始引起人们的重视,特别是需要团队共同协作的软件产品。例如,典型的瀑布模型开发过程,先由个人负责开发模块,由于开发进度不同或沟通等问题,往往等到多数模块都完成后才能开始执行集成测试,那么发现的问题数量可能会爆炸性的增长[1]。
持续集成起源于极限编程开发方法,是Martin Fowler和Matthew Foemmel于2000年提出的一项软件开发实践[2]。简单来说持续集成是指开发团队中的每个成员都尽量频繁的把工作提交到版本库,构建服务器自动将新提交的代码与原有资源构建成一个新版本,通过测试来验证新提交的代码没有对项目造成破坏[3]。
文中基于Jenkins,一种持续集成工具,实现了嵌入式软件持续集成的闭环测试过程。通过配置Jenkins、自动化测试工具、版本控制软件和测试报告插件,当检测到版本库有变化时,工具会自动下载和测试版本库中的程序,如果发现问题,则立刻通知开发人员[4]。本方案不仅能够密切监视代码库的正确性,包括代码质量和覆盖率;而且可以在无人值守的情况下自动完成预先设定好的测试任务,完成重复的测试过程,通过提供更快地识别和修复集成以及回归相关问题,达到更平滑、更快的交付和更少的问题[6]。
持续集成作为敏捷开发的最佳实践已被广泛研发团队采用,其中Jenkins作为重要的开源持续集成工具之一,为持续集成测试提供了一个方便的平台。其不仅支持多种第3方插件,而且它的配置和管理功能也能强大,包括系统配置、插件安装、日志记录和权限管理等[5]。
文中主要采用Jenkins + Subversion(SVN) + Sygwin + C++Test组成持续集成系统。其中Jenkins为持续集成服务器(CI Server);Subversion为开源的源代码管理工具,保存源代码和变更记录;Sygwin为软件开发环境,提供软件测试编译器;C++Test为一款商业测试软件,支持包括标准规则检查,以及测试用例调用、覆盖率统计和度量分析等。之所以选择商业测试软件,是由于轨道交通机车车辆软件产品在行业标准要求下,对于软件有明确的测试指标,例如:执行静态分析、动态测试,统计单元测试覆盖率等[7],一般商业软件能够同时满足这些要求。在第2节内容中,将使用嵌入式软件代码对该集成策略进行验证,在第3节测试基于Maven开发的测试报告插件。
持续集成基本实现过程如图1所示,主要包括代码提交、创建项目、构建、测试和测试结果反馈,具体步骤如下:
(1)开发人员A与B分别负责同一项目的不同模块开发,假设这些模块之间存在接口关系。当完成接口设计和基础逻辑,具备集成测试条件时,提交到源代码管理库SVN。同时,提前在测试环境下编写集成测试用例,直到测试用例能够覆盖所有需求;
(2)将被测软件提交到“源代码管理库”。SVN将记录当前提交的版本和修改时间等情况,后续的新增模块和变更都在此基础上不断纳入SVN进行管理;
(3)“创建项目”是在Jenkins中创建项目空间,和进行基本信息设置的过程,例如:源代码版本库的链接、轮询周期、触发构建条件等;
(4)当Jenkins轮询发现源代码管理库发生改变时触发“构建”过程,检查被测代码在开发环境下是否能够通过编译,如果有错误则通知开发人员,并停止测试。测试之前的编译检查,是执行测试的基本进入条件;
(5)在“自动化测试”阶段,使用自动化测试工具事先编写好的测试用例进行静态规则检查和动态测试;
(6)测试完成后,以电子邮件形式“通知”开发人员测试结论。默认情况下,Jenkins为每个失败和不稳定的构建发送一封电子邮件。
图1 持续集成流程图
SVN作为版本控制工具,项目开发人员提交代码到版本库SVN,可通过配置Jenkins触发条件,设置定时检查版本库更新情况,或者设置SVN钩子post-commit,当版本库有代码更新时,Jenkins自动下载最新代码到工作空间[8]。例如,Jenkins周期轮询使用了cron风格语法,包括5个由空格分隔的字段:
MINUTE HOUR DOM MONTH DOW
在本方案中要求每小时执行一次测试,配置方式为:“0 */1 * * *”
将开发完成的嵌入式子系统代码导入工作空间,使用gcc交叉编译器,对工程进行编译,编译过程在Makefile中实现。在构建脚本中进行判断编译是否成功,当编译发生错误时终止测试;反之,继续执行集成测试。见表1,通过make命令执行编译检查。
当工程构建通过后,启动软件测试工具C++Test对代码进行测试,由于该软件支持命令行操作,因此可以通过脚本的方式启动静态规则检查和动态测试,其中静态规则检查集通过 -config进行设置,指定要求的规则集。动态测试使用测试工具C++Test命令行- cpptestcli,指定工程路径C:...cpptestscan.bdf,调用预先设计好的测试工程。在测试用例执行过程中,如果发生错误,可查看Jenkins控制台,或者将控制台信息作为日志导出查看,使用-appconsole命令。本项目自动测试脚本见表1。
表1 Jenkins构建配置脚本
测试结束后,Jenkins输出测试结果趋势图,显示测试用例总数和失败用例数,并可查看控制台打印信息。为了方便查看详细的测试信息,本方案使用自行开发的插件,提取C++Test生成的测试报告内容,测试人员能够在Jenkins中查看测试结果见图2。报告中列出了被测项目里每个代码文件的错误和位置,可直接链接到源代码相应代码。
图2 测试报告
持续集成系统的另一个主要优势在于能够快速向开发者提供测试反馈信息。一般情况下,测试过程是定时触发且无人值守的,那么就需要测试系统在执行时,及时记录测试过程,执行完毕后,迅速反馈测试结果。Jenkins提供了3种反馈方式:Email、IM、RSS,在每次构建失败,或者第1次构建成功时会发出通知。
Jenkins已经形成了内容丰富的插件库,作为开源工具,可供测试人员实现持续集成配置。本次开发使用的IDE是MyEclipse。开发环境需要安装JDK(版本1.8.0_65)、Maven(版本3.5.2),配置环境变量、添加path路径,见图3。
图3 安装JDK与Maven
由于Jenkins是基于maven的java项目,因此其插件就是在maven框架下开发。安装maven相关扩展工具,再配置setting.xml文件,如图4,否则编译时会报错。
图4 配置Maven的setting.xml
使用maven创建一个插件项目,好处是插件可以根据pom.xml里的dependency能够自动下载相关的jar包并解决依赖问题。Maven项目的目录结构是默认的标准模板,以本插件开发目录为例(见图5),主要包括src/main/java源代码目录,src/main/test测试代码目录,src/main/resources存放插件jelly界面设计。
图5 Maven工程文件目录结构
该插件最终显示形式见图2,实现了收集、筛选和分类测试结果的功能,例如:“Warnings Trend”汇总了测试中的全部问题,“Summary”按照3个重要等级对问题进行分类,“Details”可以显示被测代码的具体错误信息,红色代表严重错误、黄色代表轻微错误。
实现了基于Jenkins对嵌入式项目进行持续集成的尝试,通过编写、配置自动化测试脚本和插件完成测试,发现代码问题16处。在修改程序后,无需提交测试人员执行回归测试,而是项目在1 h内(根据需要)自动进行构建和重新集成测试,且完成测试后能够立刻通知相关人员。此方式不仅减轻了开发和测试人员重复提交测试的工作,并且有效降低代码问题爆炸式出现的可能性,时刻监视着版本库中代码的测试状态。
方案仍有很多需要优化和尝试的方面。首先,当遇到大型项目或者需要频繁提交版本控制系统的项目,即拥有大量的构建任务时,经常轮询SCM服务器可能导致网络查询饱和构建任务排队,从而降低了测试效率。如何优化轮询时间和触发条件,需要进一步试验。另外,Jenkins更强大的功能之一是它能够跨大量的机器调度构建作业,分布式构建可以用来吸收额外的负载,应对构建作业中的高峰期。针对大型项目的配置方案,值得今后深入研究。