网站建议:179001057@qq.com

面向复杂软件的 Build 自动验证解决方案

技术2022-05-12  0

Build 验证测试(Build Verification Test)是测试过程的第一步,通常只对软件的主干功能进行初步测试,通过验证的 Build 才能转给功能测试人员进行大规模的细化测试,以此确保功能测试人员不会由于安装坏 Build 而浪费时间。

在持续集成的软件开发过程中,构建(Build)服务器一般每天都会编译最新的源代码并构建新的 build,这就要求 Build 验证也以同样的频率紧随进行。另一方面,由于软件的主干功能通常比较稳定,所以 Build 验证的重复性较强。正是因为这两个特点,各软件产品的 Build 验证都力图实现 100% 自动化。

相对 Build 的手工验证,自动验证能够带来以下好处:

让 Build 验证人员从枯燥的,简单的重复性测试中解脱出来。 自动验证的执行速度比手工验证更快,允许开发人员在最短的时间内获知新 Build 中存在的严重问题,并进行及时修复,功能测试人员也能更快获取值得信赖的新 Build 。 Build 自动验证系统 7 × 24 小时工作,不受白天黑夜以及人员假期的影响。当 Build 验证人员和开发(及功能测试)人员在时区和国度上存在差异的时候,手工验证就容易滞后。

但是,Build 验证的自动化在前期需要较大的开发工作量。另外,如何使得 Build 自动验证系统能够长期稳定运行,也是一个棘手的问题。任何一个环节的错误(例如,软件安装测试后的环境清理),都会造成系统崩溃。虽然不同软件的 Build 自动验证的具体实现五花八门,但本文力图提取其中的不变部分,给出一个具有一般性意义的 Build 自动验证解决方案。此方案已被用于 Websphere Business Monitor 的 Build 自动验证,实际运行结果证明其能在无人干预的情况下长期稳定运行。

Build 验证的自动化解决方案

源代码配置管理服务器和构建(Build)服务器是自动化开发环境的一个基本子集,构建(Build)服务器定期编译最新源代码,并生成面向测试人员的 Build 。为了在自动化开发环境中增加 Build 自动验证的功能,这个基本拓扑需要被进一步扩展。如图 1 的红框部分所示,我们添加了构建(Build)验证服务器和验证结果发布服务器。

图 1. 带 Build 自动验证的自动化开发环境

 

 

构建(Build)验证服务器

构建(Build)验证服务器运行 Build 自动验证程序。如何启动 Build 自动验证程序?一般来说,有以下两种方案:

由构建(Build)服务器触发。构建(Build)服务器每次生成新的 Build 之后,主动启动构建(Build)验证服务器上的 Build 自动验证程序。 由构建(Build)验证服务器触发。构建(Build)验证服务器定时轮询构建(Build)服务器,一旦发现新的 Build,则马上下载验证。

考虑到构建(Build)服务器的重要性,Build 自动验证应该尽量避免对它的影响。方案 1 显然要求修改构建(Build)服务器上的构建脚本,而方案 2 则对其没有任何影响,所以我们选择方案 2 。

Build 自动验证程序按图 2 所示步骤循环执行。

图 2. Build 自动验证程序

 

验证结果发布服务器

Build 验证结果需要被持久化存储,所以我们引入验证结果发布服务器,开发人员和测试人员可以通过浏览器查看所有 Build 的验证结果。图 3 展示了验证结果发布服务器的具体实现,构建(Build)验证服务器和发布服务器通过数据库交互,并通过一个 Web 程序进行结果发布;图 4 则是一个典型的验证结果发布的实例。

图 3. 验证结果发布服务器的具体实现

 

图 4. 构建(Build)验证结果发布

 

验证结果发布程序的实现相对简单,我们对此不作展开。 Build 自动验证程序则是整个解决方案的核心,下面对它的所有步骤逐一说明。

轮询下载 Build

Build 的下载一般通过 FTP 协议完成,虽然 Windows 和 Unix 的命令脚本都提供相信的 FTP 支持,但用命令脚本不易实现各种错误(例如网络错误或者 Build 服务器不可访问)的处理逻辑,因此脚本运行的容错性和稳定性相对较差。而 Build 自动验证程序要求在无人干预的情况下 7 × 24 小时工作,所以我们选择 Java 实现 Build 轮询下载。图 5 是 Build 轮询下载的基本流程,BuildDownload.java 则是该流程的具体实现。

图 5. Build 轮询下载流程

清单 1. BuildDownload.javapublic class BuildDownload { private String server; //Build Server's IP or host name private int port; //Build Server's FTP port private String user; //User ID used to log in the Build Server private String pwd; //Password private String remoteBuildDir; //The remote build directory on the Build Server private String localBuildDir; //The local directory to store the downloaded build private int interval; //The interval minutes to search the new build public static void main(String[] args) { //1. Check the input parameter: the full path of the configuration file if (args.length != 1) { System.out.println("Error - Invalid parameters."); System.exit(1); } BuildDownload dameon = new BuildDownload(args[0]); //2. Search and download the newest build boolean isSuccess = false; do { isSuccess = dameon.download(); } while(!isSuccess); } public BuildDownload(String configFile) { /*Load the specified property file and initialize the configurations*/ } public boolean download() { FtpClient client = new FtpClient(); InputStream input = null; BufferedInputStream buffer = null; BufferedReader bufferReader = null; FileOutputStream outfile = null; try { System.out.println("Info - Wait " + this.interval + " minutes."); Thread.sleep(this.interval*60000); //1. Connect to FTP server client.openServer(this.server, this.port); client.login(this.user, this.pwd); //2. Search the newest build on the Build Server System.out.println("Info - Search the newest build on the Build Server."); client.cd(this.remoteBuildDir); input = client.list(); bufferReader = new BufferedReader(new InputStreamReader(input)); String newest = "00000000000000.zip"; //The format of build name: yyyyMMddHHmmss.zip while ((temp=bufferReader.readLine())!=null) { String temp = temp.trim(); int start = temp.lastIndexOf(" "); temp = temp.substring(start+1); if ( temp.compareTo(newest) > 0 ) newest = temp; } bufferReader.close(); input.close(); System.out.println("Info - The newest build on the Build Server is " + newest); //3. Compare the remote newest build with the latest verified build String latestVerifiedBuild = getLatestVerifiedBuildID(); if (latestVerifiedBuild.compareTo(newest) >= 0) { client.closeServer(); System.out.println("Info - The build " + newest + " has been verified."); return false; } System.out.println("Info - A new build " + newest + " is ready."); //4. Download the newest build clearLocalDirectory(); System.out.println("Info - Start to download the build " + newest); client.binary(); input = (InputStream)client.get(this.remoteBuildDir + "/" + newest); buffer = new BufferedInputStream(input); outfile = new FileOutputStream(this.localBuildDir + "/" + newest, true); int content; while((content=buffer.read()) != -1) { outfile.write(content); } outfile.flush(); outfile.close(); buffer.close(); input.close(); client.closeServer(); System.out.println("Info - Finish to download the build " + newest); return true; } catch (Exception e) { e.printStackTrace(); System.out.println("Error - Unexpected exception happened."); return false; } } public String getLatestVerifiedBuildID() { /*Get the latest verified build from the database in the publish server*/ } public void clearLocalDirectory() { /*Clear the local directory in which the downloaded build was stored*/ } }

安装 Build

Build 本身必须提供命令行安装模式,具体过程如下。

解压刚下载的最新 Build 启动 silent install (安装所需的配置文件必须预先定义好) 检查安装日志,确认没有错误发生 初步验证安装结果,例如文件系统,数据库,以及创建在 Websphere 应用服务器中的各种配置对象等。

Build 成功安装是执行功能测试用例的前提。如果验证程序无法检测出 Build 安装失败,则其在继续执行功能测试用例时,测试用例必然失败,验证程序虽然仍能判断 Build 是坏的,但问题定位却是错误的:某功能测试用例失败,实际则是安装失败。

为避免这种情况的发生,就要求对实际安装结果进行适当验证。通常来说,简单的日志检查并不充分,我们还要预先搞清正确的安装结果,并以此为标准对实际安装结果进行验证,如文件系统和数据库等。

执行主干功能测试用例

Build 验证只对软件的主干功能进行初步测试,不同软件的测试用例各不相同。在测试用例的自动化开发中,需要注意以下几点:

尽量选择相对稳定的系统级接口,如各模块的命令行脚本,J2EE 应用服务器的 mbean 等。这样可以使 Build 自动验证程序长时间稳定运行,而无需频繁修改。 对测试用例执行结果进行严格验证,如检查返回代码,日志文件,以及用例生成的各种对象(如数据库记录等),以此提高对坏 Build 的问题自动定位的准确度。 避免图形用户界面(GUI)的细节测试。因为在一个完整的软件开发周期中,GUI 的实现是一个渐进的过程,因此它们的自动化测试脚本也需要经常更新。这与 Build 自动验证程序的稳定性要求相背。 避免陷入底层的 API 测试,一方面底层 API 本身并不稳定,另一方面单元测试已经覆盖底层 API 的测试。

发布 Build 验证结果

构建(Build)验证服务器需要把 Build 验证结果存储到验证结果发布服务器,两者通过数据库交互,数据库结构可参考 BVT(Build Verification Test)DDL 。

清单 2. BVT DDLcreate table BVT.RESULTS ( ---- build id ---- BUILD_ID VARCHAR(256) NOT NULL, ---- start time of BVT ---- START_TIME TIMESTAMP, ---- end time of BVT ---- END_TIME TIMESTAMP, ---- whether the build passed BVT ---- BVT_STATE SMALLINT, ---- result of the download build step ---- DOWNLOAD_BUILD SMALLINT, ---- result of the silent install step ---- SILENT_INSTALL SMALLINT, ---- result of the install verification step ---- VERIFY_INSTALL SMALLINT, ---- result of each test case ---- CASE1 SMALLINT, CASE2 SMALLINT, … … ---- defect number for the bad build ---- DEFECT_NO VARCHAR(20), ---- log.zip ---- BVT_LOG BLOB(1000M), ---- specific description for the build ---- NOTES VARCHAR(2048), primary key (BUILD_ID) );

验证结果发布通常包含以下步骤:

将测试用例的执行结果存储到验证结果发布服务器。 将 Build 验证过程的相关日志存储到验证结果发布服务器。 将 Build 验证结果通过 email 发送给相关的测试开发人员。 如果被验证 Build 是坏的,则自动在 Bug 追踪系统(如 CMVC)中生成 Defect 。

清理测试环境

Build 验证结果发布以后,必须清理测试环境,为下一个 Build 做好准备。这个步骤非常重要,由于构建(Build)验证服务器需要在无人干预的情况下 7 × 24 小时连续运行,如果环境清理不成功,则可能引起下一次 Build 自动验证的失败,甚至导致构建(Build)验证服务器发送错误的 Build 验证报告。一般来说,有以下几种清理测试环境的方法:

调用软件本身的卸载命令。由于软件开发过程中,卸载命令本身可能并不完善,出错的可能性很大,所以该方法使用较少。 直接编程删除文件系统的相关文件,数据库中的相关对象,甚至操作系统中的相关配置。该方法可靠性较好,但需要较大的开发工作量,而且在整个软件开发周期中,可能需要经常修改环境清理程序。尤其当被验证的软件需要安装在其他软件之上的时候,环境清理问题会变得更加复杂。 准备一个干净的测试环境(如果被验证软件需要安装在其他软件之上,则可事先安装好相关的软件),然后用 Ghost(或其他备份软件)做硬盘备份,清理测试环境时只要简单的从备份映像恢复即可。

方案 3 简单可靠,而且适用于复杂软件(即被验证软件需要安装在其他软件之上)。在 Windows 上,我们可以配置一个任务计划(Scheduled Task)使得自动验证程序在备份映像恢复时能够自启动。

 

 

结束语

构建(Build)自动验证可以作为自动化开发环境的一个增强环节,通过对软件主干功能的初步测试,尽快将严重错误报给开发人员,并为测试人员过滤掉不符合要求的坏构建(Build)。本文给出的 Build 自动验证解决方案具有较强的通用性,但应用到具体软件的构建(Build)自动验证时,仍需选择适当的功能测试用例,尤其要注意对测试用例执行结果的严格验证,以提高对坏构建(Build)中的问题的自动定位的准确度。


最新回复(0)