lsekfe 发表于 2016-3-18 14:22:04

【转自网络】建立复杂环境下的编译和测试自动化


简介: 本文结合 CruiseControl 和 STAF (STAX) 实现在复杂环境下的持续集成和自动化编译测试。在我们的解决方案中,CruiseControl 充当入口来定时触发编译和测试的过程。在更新完 CVS 代码后,CruiseControl 调用 STAX 任务来对代码进行分发、编译、部署、测试以及收集日志,最后日志会被集成到 CruiseControl 的控制台,用来显示给开发和测试人员。本文提出的测试方案不仅能够减轻测试人员的工作量,而且能够把测试结果及时反馈给开发人员,增强他们在开发过程中的信心。介绍持续集成能够使开发人员和测试人员在同一个最新的版本上工作。CruiseControl 是现在流行的持续集成的软件。自动化测试能够大大减轻测试人员的工作量,减少测试过程中人为出现的错误。STAF (STAX) 是一个轻量级的自动化测试框架。本文结合 CruiseControl 和 STAF (STAX) 来介绍一个复杂环境下的自动化测试方案。我们使用 CruiseControl 作为自动化测试的入口和调度器,用它来控制 STAX 任务的执行。STAX 通过 STAX 复杂对测试代码进行分发、编译、部署和测试。在整个测试过程中,我们不仅可以使用 JUnit 测试用例来测试代码,而且可以使用其他方式的测试用例来测试,比如 Rational Functional Tester 脚本,Robot 脚本等。场景介绍本文使用一个简化了的复杂应用场景作为运行自动测试解决方案的环境。 该应用由一个提供简单 Echo Service 的 WebService 和一个 Web 界面的 WebService 客户端组成。 用户既可以使用程序来直接访问该 WebService 也以通过在浏览器中操作该客户端来访问 WebService 提供的服务。在访问 WebService 和客户端之前,用户都需要经过身份验证来声明自己的身份。WebService 和客户端分别运行于 Windows 和 Linux 平台上的 WebSphere 应用服务器之上。在 WebSphere 应用服务器上需要配置登录认证模块来实现用户访问前的身份认证。在这一应用场景中,测试团队经过分析,对自动化功能测试提出了以下需求:WebService 和客户端需要分别进行功能测试。开发团队提供了用于配置应用程序登录模块 (LoginModule) 的自动配置部署脚本,该脚本被发布到一个 FTP 服务器上。
WebService 和客户端的源代码以及测试脚本的源代码都存放在 CVS 上,需要在测试执行前进行自动更新。自动化测试执行需要每天自动定时运行。测试执行的结果包括执行日志需要被测试团队和开发团队及时访问到。自动化测试方案的设计针对上述需求,我们可以将这一自动化测试方案分隔为如下图所示的几个逻辑组件:http://www.uml.org.cn/Test/images/2013122721.jpg图 1. 自动化测试方案的逻辑框图部署脚本库是指发布配置登录模块的脚本的服务器。在本文中,是一个 FTP 服务器。
源代码库是指发布被测试应用程序源代码以及测试相关源代码的服务器。在本文中,WebService 和客户端的源代码以及测试脚本的源代码都被发布在一个 CVS 服务器上。定时器设置每天特定时间触发,本文中,定时器的功能是 CruiseControl 提供的。自动编译工具将从源代码库中获取的测试代码和被测试代码进行编译。本文中,使用CruiseControl 结合 Ant 作为自动编译工具。测试自动化框架负责将编译完成的被测试代码和测试代码部署到测试环境中,并调用测试自动化执行工具来执行测试。在测试执行介绍后,框架还需要收集相应的执行日志,并将执行结果和日志交由 CruiseControl 进行发布,以供测试和开发人员参考。在本文中,测试框架采用了开源的 STAF、STAX 框架。测试执行工具是指进行测试的各种测试工具,本文中,测试工具使用了 JUnit,RFT 等。测试环境是指运行被测试代码和测试脚本的环境。在实际应用中测试环境既可能是一个简单的Windows 服务器,也可能是由许多不同平台,不同类型的服务器组成的复杂环境。本文中,测试环境包括了不同版本不同平台的 WebSphere Application Server,以及运行测试脚本的一台服务器。测试报表工具是将测试执行结果和日志进行呈现的工具。由于测试过程完全自动进行,只有将测试结果和日志尽可能清晰的展示在测试和开发人员面前,才能充分发挥自动测试的作用。从上面对各个逻辑组件的描述中可以看出,测试框架在整个自动化测试方案中位于核心位置。它像控制器一样管控着其他各个组件,同时又像胶水一样把各个组件连结起来协同工作。在分析了测试自动化需求和逻辑组件之后,我们可以给出具体的测试方案设计。http://www.uml.org.cn/Test/images/2013122722.jpg图 2. 测试方案设计实现在上图中,控制机上安装了 CruiseControl 和 STAF、STAX 框架两套工具。编译机上安装了编译工具 Ant 以及测试执行工具 Junit 和 RFT。 其中 JUnit 测试脚本用来测试 WebService,而 RFT 测试脚本则用来测试和操作客户端。测试机 1 和测试机 2 则分别用来运行 WebService 和客户端。该测试方案的具体执行步骤如下:CruiseControl 服务器在每日指定的时间自动从 CVS 服务器获取最新测试代码,完成后 CruiseControl 服务器执行一个批处理命令来启动一个 STAF 任务。该 STAF 任务从 FTP 服务器下载最新配置登录模块的脚本到本地目录。STAF 将 WebSerivce 和客户端的源代码,测试脚本源代码,以及测试代码分发到编译机上。STAF 在编译机上调用编译工具 Ant 来编译被测试代码(编译出包含 WebService 的 war 和包含客户端的 war ),测试脚本源代码。STAF 将编译出的运行时代码 (war) 通过 WAS 远程命令部署到测试机1,2中。STAF 将配置登录模块的脚本发布到测试机1,2中,并分别在测试机上运行该脚本来配置登录模块。STAF 在编译机上调用 JUnit 和 RFT 来运行测试代码。STAF 将测试结果送回控制机并显示。使用的产品和技术STAF/STAX软件测试自动化框架( Software Testing Automation Framework,简称 STAF )是一个开源的测试自动化框架,它的设计核心理念是称为“服务”的可重用组件(例如,进程调用,资源管理,日志和监控等)。 STAX 是构建在 STAF,XML 和 Python 语言之上的执行引擎,它的出现大大的简化了测试人员的实现测试自动化的工作。STAX 同时还提供了一个强大的图形界面监控程序,使用该监控程序你可以监控并控制正在 STAF 框架中执行的任务。本文STAF/STAX 脚本一章详细介绍了在如何使用 STAF/STAX。CruiseControlCruiseControl 是一个被广泛使用的用于软件持续集成的开源框架。 所谓软件的持续集成( Continuous Integration )是指一种软件开发团队频繁的将他们各自开发的组件进行集成的实践。通过软件的持续集成可以尽早的发现错误,并降低最终集成时所耗费的时间和精力。本文配置 CruiseControl 一章详细介绍了如何配置和使用 CruiseControl。JUnit/HttpUnitJUnit 是一个被广泛使用的用于测试 Java 应用程序的开源自动化测试框架。JUnit 提供了十分简洁易用的编程接口来让测试人员编写测试脚本。同时搭配使用 xUnit 系列的工具,JUnit 可以完成对许多不同类型的应用的测试。HttpUnit 是一个专门针对 Web 应用程序进行自动化测试的开源类库。它可以模拟浏览器的行为(包括表单提交,JavaScript,简单 http 认证,cookie,以及页面重定向等)来测试 Web 应用程序并且允许测试代码检查从服务器端返回的页面。在本文中,我们搭配使用 JUnit 和 HTTPUnit 这两个工具来测试 WebService。Rational Functional TesterIBM Rational Functional Tester ( RFT ) 是一个用于进行自动化的功能和回归测试的工具。它使得测试人员可以测试应用程序的图形界面,并提供了数据驱动的测试能力。 RFT 支持不同平台上的各种类型的图形界面应用,常见的包括在浏览器中运行的 Web 应用界面,用 SWT 开发的图形界面等。不仅如此, RFT 还提供了多种语言版本的测试脚本和开发环境供测试人员选择,包括在 Eclipse 环境中开发 Java 测试脚本和在微软的 Visio Studio 环境中开发 VB.net 脚本。RFT 当前最新的版本为 7.0。在本文中,我们使用 RFT 来测试 WebService 客户端。准备环境本章介绍在自动测试前的准备工作,包括下列内容:配置 WAS 环境变量,删除 WAS 上的 JAAS 登录模块脚本WAS 重启脚本在部署服务器上准备部署脚本配置 WAS 环境变量 ( WAS_HOME )在使用 WAS 开始工作前,首先要配置 WAS 环境变量 WAS_HOME,WAS_HOME 用于指向 WAS 的安装目录。下文中,在使用一些 WAS 自带的工具时,会引用环境变量 WAS_HOME 来表示 WAS 的安装目录。具体的设置方法如清单 1 所示。清单 1. 配置 WAS_HOME

set WAS_HOME=C:\Program Files\IBM\AppServer
echo %WAS_HOME%
export WAS_HOME=/opt/IBM/AppServer
echo $WAS_HOME
清单1展示了如何配置 WAS_HOME。在 Windows 平台中使用 set 命令进行设置,以后可以通过 %WAS_HOME% 进行访问。在 Linux 平台中使用 export 命令进行设置,以后可以通过 $WAS_HOME 进行访问。如果要使环境变量 WAS_HOME 在系统启动时自动设置,可以将 export 语句添加到 boot.local 文件中。删除 WAS 上的 JAAS 登录模块脚本在正式的测试之前,WAS 上的 JAAS 录模块需要更新到最新的版本。为了保证最新版本的成功更新,我们首先要将旧版本的 JAAS 登录模块删除(如果存在的话),如清单 2 所示。清单 2. 删除 JAAS 登录模块 – DeleteLoginModule.py
loginModuleAlias = "wssecurity.KerberosToken"
02 lineSeparator = java.lang.System.getProperty('line.separator')03 cells = AdminConfig.list("Cell").split(lineSeparator)
04 for cell in cells :
05 security = AdminConfig.list("Security", cell)
06 appLoginConfig = AdminConfig.showAttribute(security, "applicationLoginConfig")
07 jaasConfigEntryString = AdminConfig.list("JAASConfigurationEntry", appLoginConfig)
08 jaasConfigEntry = jaasConfigEntryString.split(lineSeparator)09 entry = None
10 for eachEntry in jaasConfigurationEntrys :
11 jlmAlias = AdminConfig.showAttribute(eachEntry, "alias")
12 if jlmAlias == loginModuleAlias :
13 entry = eachEntry
14 print "\nEntry found: " + entry
15 break16 if entry != None :
17 AdminConfig.remove(entry)
18 print "\nEntry deleted: " + entry
19 AdminConfig.save()
清单 2 展示了如何使用 wsadmin 脚本来删除 WAS 上的 JAAS 登录模块。WAS 的 wsadmin脚本支持 Jacl 和 Jython 两种类型的脚本语言,这个示例采用了 Jython 语言。其中,第 1 行设置了 loginModuleAlias 变量,用于保存待删除的 JAAS 登录模块的别名。第 2 行定义了 lineSeparator 常量,用于表示换行符。第 3-4 行获取了 WAS 中的 Cell 列表,并遍历整个 Cell 列表,对每个 Cell 进行处理。第 5-6 行获取了 Cell 中的安全配置,得到 Application Login Configuration。第 7-8 行进一步获取 Cell 中的 JAAS 登录模块的列表。第 9 行设置了 entry 变量,用于保存查找到的待删除的 JAAS 登录模块,初始值设为 None,表示未找到。第 10-15 行是一个循环,遍历 Cell 中的 JAAS 登录模块的列表。对每个 JAAS 登录模块,第 11 行首先获取它的别名,第 12 行将别名与 loginModuleAlias 变量中保存的待删除的 JAAS 登录模块的别名进行比较,如果相同则说明这个 JAAS 登录模块就是待删除的登录模块。第 13 行将该 JAAS 登录模块保存在 entry 变量中,第 14 行打印出找到待删除登录模块的日志信息,第 15 行则退出循环。如果在循环没有找到和待删除登录模块相匹配的,则 entry 变量保持为 None。第 16-19 行是删除登录模块的代码,第 16 行判断一下是否找到待删除登录模块,如果 entry 变量为 None,则说明没有找到,直接退出。反之,第 17 行执行删除,第 18 行打印出删除日志信息,第 19 行保存更改。在我们的示例中,删除 JAAS 登录模块脚本是存放在一台 Linux 系统中,我们要通过这个Linux 系统上的 WAS 来删除环境中的其它系统上的 WAS 中的 JAAS 登录模块,这个操作需要通过 wsadmin 工具来远程访问其它 WAS。调用过程如清单 3 所示。清单 3. 调用删除 JAAS 登录模块 – DeleteLoginModule.sh
$WAS_HOME/bin/wsadmin.sh
02    -conntype SOAP
03    -host 172.16.0.124
04    -username wasadmin -password passw0rd
05    -lang jython
06    -f DeleteLoginModule.py
清单 3 展示了如何通过 wsadmin 工具调用删除 JAAS 登录模块的方法。第 1 行 $WAS_HOME/bin/wsadmin.sh 为 wsadmin 工具,用于管理 WAS 以及配置、应用程序部署和服务器运行时操作。第 2 行指定使用的连接方式为 SOAP,连接方式可能的类型包含:SOAP、RMI 和 NONE。第3行指定目标 WAS 的主机名。第4行指定用来连接到远程 WAS 的用户名和密码。第5行指定脚本文件的语言采用 Jython 语言,可能的语言包括: Jacl 和 Jython 。第6行指定在目标 WAS 上要运行的脚本。(注:第 1 行到第 6 行为一条完整的命令,此处为讲解方便分解成 6 行)WAS 重启脚本在更新(安装或删除) JAAS 登录模块之后,必须重新启动 WAS,以使更新生效。每个WAS 机器上都需要准备重启的脚本,该脚本在本地运行,控制指令通过 STAF/STAX 远程发起。 WAS重启脚本,如清单 4 所示。清单 4. 重启WAS
               

%WAS_HOME%\bin\stopServer.bat server1 -user wasadmin -password passw0rd
%WAS_HOME%\bin\startServer.bat server1
$WAS_HOME/bin/stopServer.sh server1 -user wasadmin -password passw0rd
$WAS_HOME/bin/startServer.sh server1
清单4展示了如何重启 WAS。其中,stopServer.sh 用于停止 WAS。如果启用了全局安全,则需要设定连接 WAS 的用户名和密码。startServer.sh 用于启动 WAS。在部署服务器上准备部署脚本在每次测试之前,需要将 WAS 上部署的应用程序更新到最新的版本。为了保证最新版本的成功更新,我们首先要将旧版本的应用程序删除(如果存在的话),然后按照新版本的应用程序,最后启动新安装的应用程序。完整的过程如清单 5 所示。清单 5. 部署应用程序 – UpdateApplication.py
import sys02 # define uninstall application
03 def uninstallApp(appName):
04 try:
05 print '\nUninstall ' + appName + '...'
06 AdminApp.uninstall(appName)
07 AdminConfig.save()
08 except:
09 print sys.exc_type, sys.exc_value
10 print ''
11 else:
12 print ''13 # define install application
14 def installApp(appName, earFileName):
15 try:
16 print '\nInstall ' + appName + ' (' + earFileName + ')...'
17 AdminApp.install(earFileName, ['-appname', appName])
18 AdminConfig.save()
19 except:
20 print sys.exc_type, sys.exc_value
21 print ''
22 else:
23 print ''24 # define start application
25 def startApp(appName):
26 try:
27 print '\nStart ' + appName + '...'
28 appMgr = AdminControl.queryNames('type=ApplicationManager,process=server1,*')
29 AdminControl.invoke(appMgr, 'startApplication', appName)
30 AdminConfig.save()
31 except:
32 print sys.exc_type, sys.exc_value
33 print ''
34 else:
35 print ''36 # parse command line arguments
37 argLen = len(sys.argv)
38 appName = ''
39 if argLen > 0:
40 appName = sys.argv
41 earFileName = appName + '.ear'
42 if argLen > 1:
43 earFileName = sys.argv44 # update application
45 uninstallApp(appName)
46 installApp(appName, earFileName)
47 startApp(appName)
清单 5 展示了如何在 WAS 上重新部署应用程序。其中,第 1 行导入了系统函数库 sys。第 2-12 行定义了函数 uninstallApp,用于卸载已安装的应用程序。第 3 行是函数的声明,该函数包含一个参数 appName,即待卸载的应用程序名称。第 4、8、11 行是一个 try…except…else… 的异常处理模块,用于对卸载过程中发生的任何异常进行处理,并将异常信息返回给用户。第 6 行执行卸载操作,第 7 行保存卸载操作的更改。如果在卸载过程中发生异常,第 9 行将异常的类型和异常的信息显示给用户,第 10 行告知用户操作失败。反之,第 12 行告知用户操作成功。第 13-23 行定义了函数 installApp,用于安装新的应用程序。第 14 行是函数的声明,该函数包含两个参数: appName 为待安装的应用程序名称, earFileName 为待安装的EAR包的文件名。第 17 行执行安装操作。其它代码与函数 uninstallApp 中的相应代码类似。第 24-35 行定义了函数 startApp,用于启动应用程序。第 25 行是函数的声明,该函数包含一个参数 appName ,即待启动的应用程序名称。第 28 行获取 Application Manager 对象,第 29 行执行启动操作。其它代码与函数 uninstallApp 中的相应代码类似。第 36-43 行解析命令行参数,第一个参数为应用程序名称,是必须的;第二个参数为应用程序EAR 包的名称,是可选的,如果省略,则在应用程序名称后加上 ’.ear’ 来代替。第 44-47 行更新应用程序,首先删除现有的应用程序,然后按照新的应用程序,最后启动新安装的应用程序。在我们的示例中,部署应用程序脚本是存放在一台 Linux 系统中,这台 Linux 系统称为部署服务器。我们要通过部署服务器上的 WAS 来更新环境中的其它系统上的 WAS 中的应用程序。这个调用过程是通过 wsadmin 工具来完成的,脚本和“调用删除 JAAS 登录模块”脚本类似,请参见清单 3,此处不再赘述。STAF/STAX 脚本STAF/STAX 脚本包括以下部分:1、环境清理2、下载配置 WAS 登录模块脚本3、分发和运行配置 WAS 登录脚本4、重启 WAS5、复制源码到编译机6、控制编译机编译源码7、复制 WAR 到部署机器上并部署8、调用测试代码9、收集日志下面我们分别介绍每个部分的脚本。环境清理在进行自动化编译、部署和测试之前,需要对上次测试的环境进行清理,以除去上次测试的残留,保证测试结果的正确和合理。首先我们需要调用第 4 章的 WAS clean 脚本来删除 WAS 中的登录模块。清单 6. STAX 调用 WAS clean 脚本示例
<script>
2   machineList = [‘was602_Linux’, ‘was61_Linux’, ‘was602_Windows’, ‘was61_Windows’]
3 </script>
4 <paralleliterate var=”machine” in=”machineList”>
5   <if expr=“machine.find(‘Linux’)>=0 ”>
6   <sequence>
7       <stafcmd>
8         <location> ‘%s’ % machine </location>
9         <service> ‘process’ </service>
10      <request> ‘start command "/root/script/CleanupEnv.sh" username "root" password
11         "password" workdir "/root/script" wait stdout /root/script/cleanupEnv%s.log'
12         % machineName
13      </request>
14      </stafcmd>
15    </sequence>
16</if>
17<if expr=“machine.find(‘Windows)>=0 ”>
18    <sequence>
19      <stafcmd>
20      <location> ‘%s’ % machine </location>
21      <service> ‘process’ </service>
22      <request> ‘start command "C:/script/CleanupEnv.bat" username "Administrator"
23          password "password" workdir "C:/script" wait stdout
24          C:/script/cleanupEnv%s.log' % machineName
25      </request>
26      </stafcmd>
27    </sequence>
28   </if>
29 </paralleliterate>
在清单 6 中,我们把系统分成了两套环境,Windows 系统和 Linux 系统,针对于不同的环境,cleanupEnv 脚本有不同的路径和后缀名。首先 4-29 行使用了并行的遍历来分别在各个系统上调用 cleanupEnv 脚本。在第 5 行和第 17 行中,if 语句使用的 expr 用 python 来解析,因此可以用 python 来写一些复杂的比较和判断语句。find 函数表示在字符串中查找指定的字符串。5-16 行针对于 Linux 的系统,使用 root 用户来调用 CleanupEnv.sh 脚本。17-28 行针对于Windows 系统,使用 Administrator 用户来调用 CleanupEnv.bat 脚本。CleanupEnv 脚本有两种储存方法:事先存放在各个测试机器上存放在 CruiseControl 控制机器上,然后由 STAX 来根据环境来分发这些脚本。一般情况下,推荐使用第二种方法。脚本分布在不同的机器上,如果发生一点小的改动,极易引起脚本的不同步。使用第二种方法,不仅会避免这个问题,而且只需改动一个脚本,将极大的节省测试人员的工作量。使用第二种方法只需在第 7 行和第 19 行前面加上 STAF 复制文件的命令即可。在 Linux 上如下所示:清单 7. STAX 分发 CleanupEnv 脚本示例
<stafcmd>
<location> ‘%s’ % machine </location>
<service> ‘fs’ </service>
<request> ‘copy FILE "D:/sample/CleanupEnv.sh" TODIRECTORY /root/script/’
</request>
</stafcmd>
把文件复制到 Linux 上需要注意一个问题,就是文件的某些属性可能没有被复制,比如可执行属性,因此需要使用下面的命令来更改文件的属性,以便用户能够执行此文件。清单 8. STAX 更改文件属性
<stafcmd>
<location> ‘%s’ % machine </location>
<service> ‘process’ </service>
<request> ‘start command "chmod +x /root/script/CleanupEnv.sh"’
</request>
</stafcmd>
下载配置 WAS 登录模块脚本在删除上次自动化测试配置的登录模块之后,我们需要从 FTP 下载最新版本的配置登录模块的脚本。本文使用 Windows 自带的 FTP 命令来完成脚本的下载。FTP 上存放了不同时期的版本,每个版本使用文件名+时间戳的方式来区分,而 FTP 命令并没有提供下载最新脚本的功能,因此我们需要自己来判断那个脚本是最新的。首先利用 FTP 的 ls 命令来列出所有的版本文件,通过 java 程序来判断哪个是最新的版本,最后再利用 FTP 命令来下载最新的版本。STAX 脚本如下所示:清单 9. 下载最新的版本文件
<sequence>
<process>
<location>'local'</location>
<command>'ftp'</command>
<parms>'-s:C:/Sample/ftpconf/ftplist.conf'</parms>
<workdir>'C:/Sample'</workdir>
</process><process>
<location>'local'</location>
<command>'java'</command>
<parms>'-cp . fileParsing.ParseBuildTime temp.txt ftptemplate.conf
ftpdown.conf'</parms>
<workdir>'C:/Sample'</workdir>
</process>

<process>
<location>'local'</location>
<command>'ftp'</command>
<parms>'-s:C:/Sample/ftpconf/ftpdown.conf'</parms>
<workdir>'C:/Sample'</workdir>
</process>
</sequence>
其中第一个 Process 命令从 FTP 服务器上获取所有的版本文件信息,保存到 temp.txt 文件中。ParseBuildTime 用来从版本文件列表 temp.txt 中找出最新的版本文件,并且根据提供的 FTP 模板文件 ftptemplate.conf 生成 FTP 脚本 ftpdown.conf,供 STAX 脚本调用下载最新的版本文件。第三个 Process 命令调用 ftpdown.conf 从 FTP 服务器下载最新的版本文件。清单 10 显示了 FTP 模板文件,它是由一系列 FTP 命令组成。清单 10. FTP 模板脚本
open ftp.ibm.com
user
password
binary
prompt
cd /sampledirectory
lcd C:\Sample\build
get "<template>" ConfigureLoginModule.zip
bye
分发和运行配置 WAS 登录模块脚本
本节把下载的最新版本文件解压缩后分发到 WAS 平台。解压缩使用 Winrar 命令来进行,如下所示:清单 11. 使用 Winrar 命令解压缩
<process>
<location>'local'</location>
<command>'C:\Program Files\winrar\winrar.exe'</command>
<parms>'x -o+ C:/Sample/build/ConfigureLoginModule.zip'</parms>
<workdir>'D:/STAFDirectory/build/'</workdir>
</process>
如何分发文件和清单 7 类似,这里不再赘述。将配置 WAS 登录模块脚本分发完成后,需要调用它完成 WAS 登录模块的配置。调用脚本的STAX 代码与清单 6 类似,这里也不再赘述。重启 WAS在完成 WAS 登录模块配置后,需要重启 WAS 来使配置生效。这里调用第三章的 WAS 重启脚本来完成WAS服务器的重启。在调用 WAS 重启脚本之前,需要分发此脚本到每个 WAS 服务器上。过程类似于清单 6 和清单 7 。有一点不同的是, WAS 重启脚本可能会需要运行较长一段时间,比如 5 分钟,因此 STAX 命令中的 wait 关键字不能忽略,表示需要等待 WAS 重启结束,如下所示:清单 12. STAX 重启 WAS 脚本示例
<stafcmd>
<location> ‘%s’ % machine </location>
<service> ‘process’ </service>
<request> ‘start command "/root/script/RestartServer.sh" username "root" password
    "password" workdir "/root/script" wait stdout /root/script/RestartServer%s.log'
   % machineName
</request>
</stafcmd>
复制源码到编译机在启动测试过程之前,CruiseControl 会调用 Ant 脚本从 CVS 下载最新的源码,包括 Web Service 源码、测试代码和 RFT 代码。在测试之前,我们需要将它们复制到编译机上进行编译,因为某些代码需要依赖于 WAS 或者 RFT 平台。复制代码分为三个部分:复制 Web Service 源码(被测试代码)。复制测试代码 (Web Service 客户端以及 JUnit 代码)复制 RFT 代码。复制过程和清单 7 类似。控制编译机编译源码每个部分的源码都有 build.xml 文件,所以只需要调用 Ant 命令来编译即可。清单 13. 编译源码
<stafcmd>
<location>'%s' % machine</location>
<service>'process'</service>
<request>'start command "c:/apache-ant-1.6.5/bin/ant.bat -buildfile %s/build.xml
> %s/build.log"username "Administrator" password "password" workdir %s wait'
% (sourceLocation, sourceLocation, sourceLocation)
</request>
</stafcmd>
与编译 Web Service 源码和测试代码不同的是, RFT 代码需要指定 RFT 安装的位置,这通过传递 Ant 参数来实现,如清单 14 所示。清单 14. 编译 RFT 测试脚本
<stafcmd>
<location>'%s' % machine </location>
<service>'process'</service>
<request>'start command "c:/apache-ant-1.6.5/bin/ant.bat -buildfile %s/buildRFT.xml
-DRFTDriver=%s > %s/build.log"username "Administrator" password "password"
workdir %s wait' % (sourceLocation, RFTDriver, sourceLocation, sourceLocation)
</request>
</stafcmd>
复制 WAR 到部署机器上并部署编译完成后,需要将编译生成的 WAR 文件到部署机器上并且通过部署机器来远程部署所有的WAS 服务器。清单 15.部署 WAR
<stafcmd>
<location>'%s' % machine </location>
<service>'process'</service>
<request>'start command "/root/script/deploywar.sh" username "root"
password "password" workdir "/root/script/work" wait
stdout /root/script/deploy.log STDERRTOSTDOUT'
</request>
</stafcmd>
我们选择通过一台部署机器来部署 WAR 的原因是这样做维护比较简单,如果 WAS 服务器IP 改变或者增加时,只需要更改部署服务器的部署脚本即可,甚至对 STAX 脚本也不用更改。当然也可以选择每台 WAS 分别对自己部署 WAR,这样当 WAS 服务器增加时,需要对 STAX 脚本做一点更改,添加相应的 WAS 服务器。在编译机器上调用测试代码部署完成后,在编译机器上调用测试代码(包括 RFT 代码)来测试示例 Web Service 程序。调用代码有两种方式:STAX 直接调用远程机器上的 Java 命令来执行STAX 调用远程机器上的 Ant 脚本来执行为了演示这两种方式的用法,我们使用 Ant 脚本来调用 JUnit 测试代码,使用 Java 命令来调用 RFT 代码。但在实际使用过程中,我们推荐使用 Ant 脚本来调用。清单 16. 调用测试代码
<stafcmd>
<location>'%s' % machine </location>
<service>'process'</service>
<request>'start command "c:/apache-ant-1.6.5/bin/ant.bat -buildfile %s/runTest.xml
> %s/runTest.log"username "Administrator" password "password" workdir %s wait'
% (sourceLocation, sourceLocation, sourceLocation)
</request>
</stafcmd>
<stafcmd>
<location>'%s' % machine </location>
<service>'process'</service>
<request>'start command "java -cp "%s\bin\rational_ft.jar" -Drational_ft.install.dir
="%s\bin" com.rational.test.ft.rational_ft -datastore "%s" -rt.log_format "html"
-rt.bring_up_logviewer false -playback testscript.VerifyWebService
>%s/runRFTTest.log"username "Administrator" password "password" workdir %s wait'
% (RFTLocation, RFTLocation, sourceLocation, sourceLocation, sourceLocation)
</request>
</stafcmd>
第一个 STAFCMD 使用 Ant 来调用 Junit 测试代码,可以看出命令比较简单。而第二个STAFCMD 使用 Java 来调用 RFT 测试脚本,命令要复杂得多,这样会给维护带来一定的困难,因此推荐使用 Ant 来编译和调用。收集产生的日志文件在部署和测试完成后,控制机器需要遍历每个 WAS 服务器(包括编译机和部署机)来收集日志文件,也就是复制文件到控制机器上。这与清单 7 类似。至此,我们已经完成了这个 STAX 脚本的编写。需要指出的是,所有的 STAX 脚本只需要在控制机器上执行即可,部署机、编译机以及测试机器完成不知道这些代码的存在,它们只需要安装 STAF 并且信任控制机器即可。配置 CruiseControl安装 CruiseControlCruiseControl 是一个非常容易使用的持续集成工具。读者可以从它的官方网站下载最新版的CruiseControl 2.7 安装包进行安装。由于安装十分简单,本文略过其安装过程。验证 CVS 配置在 Dos 命令行窗口中运行如下命令来验证你可以从 CVS 检出源代码 (假定我们使用 cygwin来连接 CVS )
cd <build_home>
set CVS_RSH=c:\cygwin\bin\ssh.exe
\progra~1\cvs\cvs -d :ext:user@cvshostname:/cvsroot/sampleproject/ \
checkout -d <build_home> Build
配置完成 CVS 后,在 build.xml 中添加如下任务:清单17. 检出 CVS 上测试源代码的 ant 脚本
<target name="getSrc">
       <echo message="Extracing source from CVS in to ${build.src.dir}" />
       <cvs
            cvsRoot=":ext:user@cvshostname:/cvsroot/sampleproject/"
            command="checkout -P"
          cvsRsh="c:\cygwin\bin\ssh.exe"
          package="src/test"
          dest="${cvs.dir}"/>
       <copy todir="${src.dir}">
          <fileset dir="${cvs.dir}/src/test>
             <exclude name="**/CVS/**"/>
          </fileset>
       </copy>
   </target>
注意:用户也可以直接通过配置使用 CruiseControl 来检出和编译源代码。但是由于在本文的场景中,编译这些测试代码需要有特定的类库支持,而 CruiseControl 所处的控制机没有安装这些类库。因此在本文中我们使用一个 ant 脚本来将代码从 CVS 检出并在后面的步骤中将其发布到编译机上进行编译。在 CruiseControl 中配置定时器为了让测试脚本能够每天定时运行,需要在 CruiseControl 中配置定时器。在 CruiseControl 的安装目录中找到 “config.xml” 并添加下面的配置代码:清单 18. 在 CruiseControl 中配置定时器
<schedule interval="300">
<composite time="0030">
<ant anthome="C:\Program Files\CruiseControl\apache-ant-1.6.5" target="buildAll"
                        buildfile="d:\ build\build.xml"/>
<exec command="d:\STAFDirectory\script\execute.bat" />
</composite>
</schedule>
上述的设置设定了 1 个定时器,在凌晨 3 点执行。定时器先运行一个 Ant 脚本将代码检出复制到一个本地文件夹中,再运行一个批处理命令 “execute.bat” 来启动后续的 STAF 命令。启动 STAX 脚本当测试脚本源代码被检出到一个本地文件夹后,批处理命令 “execute.bat” 被执行。这个批处理命令只有一行代码,就是执行一个 STAX 任务:
staf local stax execute file D:\\STAFDirectory\\script\\main.xml wait
这个 STAX 任务已经在前面的章节中做了详细的介绍,此处不再重复。合并 JUnit 测试日志在本场景中,当测试运行完成后测试的结果和日志需要被发布到 CruiseControl 的 Web 控制台中。但是由于测试执行是在另一台服务器上完成的,因此 CruiseControl 所在的控制机不能直接访问这些日志。我们只有创建另一个 STAX 任务把测试日志从执行测试的编译机上复制到控制机上。CruiseControl 内建了对 JUnit 产生的 XML 格式的日志的支持。为了让 CruiseControl 能够正确的显示测试日志,需要在其配置文件 “config.xml” 中添加如下设置:清单19. 在 CruiseControl 中合并 JUnit 测试日志
<log>
<merge dir="projects/${project.name}/target/test-results" />
</log>   
发布 RFT 日志和编译的运行时代码到 CruiseControl 控制台RFT 运行的日志文件有两种类型,纯文本日志和 HTML 格式的日志。CruiseControl 目前还不支持直接显示 RFT 日志文件。因此我们只能把 RFT 产出的 HTML 日志文件作为一种编译产出放置在特定文件夹下。这样用户可以通过 CruiseControl 控制台直接访问该 HTML。在编译机上编译得到的 WebService 和客户端的 war 文件作为一个编译产出也被存档保留,并通过 CruiseControl 发布。当测试发现缺陷后,测试员可以通过这些 war 文件来追溯到某一特定版本的编译产出并重现缺陷,开发人员则可以在这一基础上进行调试和修正。添加如下代码到 CruiseControl 配置文件 “config.xml” 中:清单20. 在 CruiseControl 中发布 RFT 测试日志
<publishers>
<onsuccess>
        <artifactspublisher dest="artifacts/${project.name}"
          dir="projects/${project.name}/target/buildArtifacts" />
</onsuccess>
</publishers>
JUnit Test Case 示例本章介绍用于测试 Web Service 的 JUnit Test Case 示例,这个例子将详细说明如何利用HttpUnit 来测试 Web Service。在这个例子中,共有三方参与,分别为 Web Service Provider、Web Service Client 和 Test Client。首先由 Test Client 向 Web Service Client 发出 HTTP 测试请求,Web Service Client 在验证用户身份后,生成相应的 SOAP 请求发给 Web Service Provider,Web Service Provider 同样在验证用户身份后为其提供 Web Service 服务,并将结果仍以 SOAP的形式返回到 Web Service Client,再由 Web Service Client 以 HTTP 形式返回结果给 Test Client。最后由 Test Client 对返回结果的正确性进行判断并给出测试结果。JUnit Test Case 示例如清单 21 所示。清单 21. JUnit Test Case 示例
01 public class SampleTest extends TestCase {
02 private String invoke_client(
03 String requestString, String clientURL, String providerURL) {
04 String result = null;
05 String requestURL = clientURL + "?endpoint=" + providerURL
06 + "&requestString=" + requestString;
07 WebConversation wc = new WebConversation();
08 wc.setAuthorization(SampleConst.Username, SampleConst.Password);
09 WebRequest req = new GetMethodWebRequest(requestURL);
10 WebResponse resp;
11 String responseString;
12 try {
13 resp = wc.getResponse(req);
14 responseString = resp.getText();
15 } catch (AuthorizationRequiredException e) {
16 result = "Invalid UserID or Password.";
17 return result;
18 } catch (Exception e) {
19 result = "Exception on HTTP request";
20 return result;
21 }
22 int checkNames = responseString.indexOf("User names strings match");
23 int checkRequest = responseString.indexOf("Request strings match");
24 if ((checkStrings == -1) | (checkNames == -1) | (checkRequest == -1)) {
25 result = "Strings not match";
26 }
27 return result;
28 }
29 public void test_sample_case_1() {
30 String requestString = "sample_case_1";
31 String result = invoke_client(requestString,
32 SampleConst.ClientWebURL,SampleConst.ProviderWebURL);
33 assertTrue(result, result == null);
34 }
35 }
清单 21 展示了访问 Web Service 的 JUnit Test Case 示例。其中,第 2-29 行定义了 invoke_client 函数,通过 WebConversation 对 Web Service 进行访问。第 5-6 行设置了 Web Service Client 的地址,endpoint 为 Web Service Provider 的地址。第 8 行为访问 Web Service 添加了身份认证信息。第 12-21 行访问 Web Service,将结果存入 responseString。如果在访问过程中发生异常,则直接返回错误信息。第 22-28 行对返回结果进行验证,如果发现结果错误,则返回错误信息。否则返回 null 表示访问成功。第 30-35 行为一个 Test 的例子,通过调用 invoke_client 对 Web Service 进行测试,并验证返回的错误消息是否为 null。如果为 null 表示测试成功,反之则失败。结论本文使用 CruiseControl、STAF(STAX)、JUnit、Rational Functional Tester 通过一个简化的具体场景展示了自动化测试平台的实现。这个平台不仅可以大大简化测试人员的工作量,减轻测试人员的工作负担,也能保证开发版本的稳定性,使开发人员尽早知道新版本的问题,增强开发人员的信心。本文仅代表作者本人观点,不代表 IBM 公司观点。



页: [1]
查看完整版本: 【转自网络】建立复杂环境下的编译和测试自动化