简介:在作为系列的最后一篇覆盖的部分是缺陷生命周期的最后一个环节,缺陷的验证。本文主要描述了如何通过 Rational Team Concert(RTC)、Rational Quality Manager(RQM)及 IBM Workload Deployer(IWD)实现缺陷验证的自动化,而且笔者通过一个 RTC web 插件来展现自动化页面。
背景
系列前两篇中我们描述了如何用 IBM 产品帮助开发人员快速重现缺陷问题,下面本文主要描述一下我们是如何使用 IBM 产品加速缺陷验证过程的。
在缺陷验证的过程中,测试人员需要完成一下几个任务:
● 定位可验证的缺陷 (Verifiable Defect)
▲ 可验证的缺陷是指缺陷对应的产品代码变动已经被开发人员提交并包含在产品最新版本中。
▲ 定位的范围一般包括
→ 测试人员自己提交的缺陷(Created by)
→ 由其他人员提交的,需要测试人员关注的缺陷(Subscribed by)
▲ 测试人员一般需要针对每个关注的缺陷查看是否相应的代码变更已经包括在最新产品版本中。
● 快速部署缺陷验证的测试环境
▲ 环境准备一直是比较耗时的部分,不过这里我们可以借助在第一篇中我们创建的重现缺陷用的 IWD 中的虚拟系统模式 (Virtual System Pattern)。
● 调用对应的测试用例执行
▲ 测试人员需要找到对应的测试用例,在最新产品版本中重现执行。
● 更新缺陷状态和信息
▲ 根据最新的测试执行结果,可以判断缺陷是否还存在,从而更新缺陷状态和信息记录。
本文主要解决的问题
● 如何快速定位可验证的缺陷
● 把缺陷验证环节自动化,自动化内容包括:
▲ 准备测试环境
▲ 执行测试用例
▲ 更新缺陷状态和信息
● 更好的用户体验
▲ 现在每个团队都会在不同程度上通过自动化工具来解决上述问题。由于上述工作涉及存储不同信息的软件系统,测试人员就需要登陆不同的系统采取相应的操作,例如:
→ 缺陷管理系统:开发和测试人员在这里可以更改缺陷的内容和状态。
→ 产品 build 管理系统:每个产品在正式发布前,内部的研发解决都会采用某个软件或系统对产品代码进行管理并且对内部发布产品非正式版本 (Build) 用于开发和测试。产品 Build 管理系统会记录每个 Build 所包含的代码变动,也会同时记录这一代码变动是由哪个缺陷引起的。
→ IT 环境管理系统:这一系统通常会为开发测试人员提供可用的环境。在某些团队中会通过不同的软件来调用 IT 环境管理系统来实现不同程度的自动化环境部署。
→ 测试管理系统:存储测试用例,测试结果和测试计划等内容。
▲ 所以开发和测试人员在完成缺陷验证工作的过程中,需要登录各种不同的软件系统,而没有统一入口的使用方式会带来不好的用户体验。
实现框架
图 1. 实现框架
框架中有四个主要的部分:
1、搜索:这个部分负责跟缺陷管理系统和产品 Build 管理系统交互获得跟用户有关的可验证缺陷信息、对应的产品 Build 信息和测试用例的信息。
2、部署环境:负责接收搜索部分提供的结果,准备测试环境。框架中给出了两个部署环境的方案,第一个是创建一个新的测试环境;第二个是查找跟这个用户有关的已经存在的测试环境,并把产品的最新 Build 部署到这个环境中。这里第二个方案也是为了能够更好地重用测试资源,以免造成浪费。
3、测试执行:根据搜索部分提供测试用例,调用对应的自动化测试用例脚本进行验证。
4、状态更新:根据测试执行的结果自动更新缺陷的状态和内容。
框架中提到的不同软件系统,每个团队可以针对自己的实例完成实现。
实现部分 1:搜索可验证的缺陷
在 RTC 中我们的实现是开始于 RTC 中的一个缺陷查询 (Query),用户可以自己定义一个缺陷查询包括那些状态时 Resolved 的缺陷。
图 2. RTC 中的缺陷查询
清单 1. 引用 RTC 中用户自定义的缺陷查询
搜索条件包括三个方面,
● 产品– 由于 RTC 同一个项目中可能会涉及多个产品的缺陷记录,而一个用户又有可能会对多个产品的缺陷负责,所以产品作为第一个筛选条件。
● 产品版本– 由于 RTC 同一个项目中可能会涉及同一个产品的不同版本的开发以及缺陷记录,而一个用户又有可能会工作于不同的产品版本。
● 环境 -- 在 RTC 中可以为不同的项目缺陷记录定义不同的字段,这里可以定义环境的信息。(如图3)
图 3. RTC 缺陷中的环境字段
根据用户自定义的缺陷查询和选定的三个筛选条件,我们可以完成第一次筛选,得到待验证的缺陷列表。下面就需要去版本控制系统中查找当前产品版本中包含哪些待验证的缺陷。
图 4. 筛选可验证的缺陷
由于本文作者没有直接使用 RTC 作为版本控制软件,而是采用其他软件进行版本控制,所以这里没有具体介绍图 4 中代码的实现。
实现部分 2:缺陷验证环境的部署
由于我们在系列第二篇中曾经提到对 RTC 中存储的缺陷添加"IWD Pattern"字段,用于存储虚拟系统模式的名字,这里可以直接通过 RTC RESTAPI 获得虚拟系统模式名称,然后通过 IWD 的命令行工具 (Command Line Tool) 或者 RESTAPI 在对应的 IWD 服务器上创建缺陷验证环境。
IWD 提供的命令行工具可以直接从 IWD 的登陆界面上下载:
图 5. IWD 登陆界面中命令行工具下载页面
图 6. IWD 命令行工具本地文件结构
IWD 命令行工具是通过 python 脚本调用 IWD RESTAPI 实现具体功能的,工具中的 readme 文件具体描述了如何调用 python 脚本,或者读者也可以从参考资源中提供的链接学习如何调用。以下是两个 python 脚本,在部署缺陷环境前先查找缺陷中记录的虚拟系统模式是否包含在这个 IWD 服务器中,如果在则继续部署这个虚拟系统模式为一个虚拟机环境。
清单 2. 查看 IWD 服务器上所有虚拟系统模式信息
# # For each pattern returned, the name of the pattern is presented # import ConfigParser import threading, time, csv, random # get all the vsystems associated with a pattern # this emulates the user clicking on the "Systems" selection in the tree view def get_systems(pattern, fname, fhandle): nsystems = 0 start = time.time() nSystems = len(pattern.virtualsystems) for cnt in range (nSystems): try: system = pattern.virtualsystems[cnt] except: break finish = time.time() fname.writerow([time.strftime("%m/%d/%Y"), time.strftime("%H:%M:%S"),'system', nSystems, (finish-start)]) fhandle.flush() # get all the patterns # this emulates the user clicking on the "Patterns" selection in the tree view def get_patterns(fname, fhandle): start = time.time() patterns = deployer.patterns finish = time.time() fname.writerow([time.strftime("%m/%d/%Y"), time.strftime("%H:%M:%S"),'pattern', len(patterns), (finish-start)]) fhandle.flush() for pattern in patterns: if (len(pattern.virtualsystems) > 0): time.sleep(random.randint(1, 10)) get_systems(pattern, fname, fhandle) ###### config = ConfigParser.RawConfigParser() config.read('listPatterns.cfg') try: output = '%s_%s.csv' % (config.get('Main', 'outfile'),time.strftime("%Y%m%d@%H%M%S")) interval = config.getint('Main', 'interval') duration = config.getint('Main', 'duration') except ConfigParser.Error: print "Error reading config file" sys.exit fhandle = open(output, 'w') fname = csv.writer(fhandle, delimiter=',') fname.writerow(['date', 'time', 'type', 'number','duration']) fhandle.flush() print 'Running ...' print 'Output File: %s\tDuration: %d\tInterval: %d' % (output, duration, interval) end_time = time.time()+(60*duration) while (end_time > time.time()): start = time.time() time.sleep(random.randint(1, 10)) get_patterns(fname, fhandle) # only sleep the remainder of the interval time.sleep((time.time()+interval) - start) print 'Completed on %s at %s' % (time.strftime("%m/%d/%Y"), time.strftime("%H:%M:%S")) fhandle.close() |
清单 3. 部署制定的虚拟系统模式
import time, threading, Queue, csv # deploy the images for performance throughput class waitfor(threading.Thread): def __init__(self, system, log_q): self.system = system self.log_q = log_q threading.Thread.__init__(self) def run(self): start = time.clock() self.system.waitFor() finish = time.clock() self.log_q.put_nowait([time.strftime("%m/%d/%Y"), time.strftime("%H:%M:%S"), self.system.name,self.system.currentstatus,(finish-start)]) def run_test(instances, fname): log_q = Queue.Queue() threads = [] for instance in range (int(instances)): virtualsystem = None sysName = '%s-%s-%s-%d' % (pfx, time.strftime("%Y%m%d"),time.strftime("%H%M5%S"), instance+1) print 'deploying virtual system: %s' % sysName createParms['name'] = sysName virtualsystem = deployer.virtualsystems << createParms sys = waitfor(virtualsystem, log_q) sys.start() threads.append(sys) #wait until all are done for thd in threads: thd.join() # write out the results fname = csv.writer(open(fname, 'w')) fname.writerow(['date', 'time', 'system_name','status','duration']) for xx in range(log_q.qsize()): data = log_q.get() fname.writerow(data) ################################################################# # select pattern to deploy instances = raw_input('number of instances to create: ') pfx = raw_input('prefix for created systems: ') output = raw_input('results file (** will be overwritten **): ') createParms = {} # default parameters createParms['*.script-4.CHEF_NODE'] = 'devops_default' createParms['*.script-4.DEPS_FILE_URL'] = '/tmp/devops_install/media/Deps-devops_services' createParms['*.script-4.CHEF_NODE_ATTR'] = '\"devops_server\"\: {\"app_source\"\:\"file:///tmp/devops_install/media/devops_services_app.zip\"}, \"ram\"\:{\"server_url\"\: \"http://devops.rtp.raleigh.ibm.com/downloads/RAM-Server-7.5.1.1-Linux64.zip\", \"persist_url\" \:\"http://devops.rtp.raleigh.ibm.com/downloads/RAM-Data-7.5.1.1.tar.gz\", /\"database_url\"\: \"http://devops.rtp.raleigh.ibm.com/downloads/RAM-Database-7.5.1.1.tar.gz\"}, \"db2\"\:{ \"host_server_url\"\:\"http://devops.rtp.raleigh.ibm.com/downloads\"}, \"jruby\"\:{\"download_url\"\:\"http://devops.rtp.raleigh.ibm.com/downloads/\"}' createParms['cloud'] = deployer.clouds[0] # select the performance pattern to deploy pattern = None while not pattern: i = 1 for p in deployer.patterns: print '%d. %s' % (i, p.name) i = i + 1 x = raw_input('select the test pattern to deploy: ') try: pattern = deployer.patterns[int(x) - 1] except: # try again pass createParms['pattern'] = pattern #start deploying started = time.strftime("%m/%d/%Y at %H:%M:%S") run_test(instances, output) completed = time.strftime("%m/%d/%Y at %H:%M:%S") print '############# test summary #######################' print 'Started on: %s\tCompleted on: %s' % (started, completed) print 'Images deployed: %s' % instances |
实现部分 3:测试执行
测试执行的时候我们需要调用 RQM 的命令行执行工具 (Command Line Execution Tool),这个工具是跟着 RQM 同时发布在 Jazz.net 上的,具体下载请看参考资源中提供的链接。
RQM 命令行执行工具是我们可以方便的执行测试,通过 RQM RESTAPI 启动测试执行记录 (Test Execution Record) 的运行。在运行测试执行时需要提供以下信息:
● RQM url – https://hostname:9443/qm
● 用户名和密码 -- 使用这个用户名允许运行相应的测试执行记录
● 项目– 测试用例所在的 RQM 中的测试项目
● 测试执行记录的 id
● 测试执行记录关联的脚本的 id
● 执行适配器 (Adapter) 的 id
▲ 执行适配器是 RQM 提供的用以连接并操作具体测试脚本的功能,常用的适配器包括 Rational Functional Tester、Rational Performance Tester、Rational Build Forge 以及其他更多第三方厂商和开源工具的适配器。用户也可以自己编写适配器。
● 执行时需要的参数
通过命令行调用后执行结果是会输出到命令行的,也可以直接输出到文件,用于后续操作。
实现部分 4:自动化中注意事项
要把整个流程自动化需要用到流程工具,可以用开源的 ant 或者使用 IBM Rational Build Forge。
这里主要介绍几个重点:
1、测试用例相关信息获得
a)缺陷可以通过 RTC 与 RQM 之间的 OSLC 关联关系连接 RQM 中的测试执行结果 (Test Execution Result),测试执行结果是测试执行记录 (Test Execution Record) 的执行结果。
b)然后我们可以从缺陷中获得测试执行结果的 url,经过分析我们可以获得 RQM 项目名称,测试执行结果 id 等信息,用于调用 RQM 命令行执行工具。
2、缺陷验证环境信息的传递
a)缺陷验证环境是我们通过部署虚拟系统模式生成的新的虚拟机,所以 ip、hostname、用户名和密码都是新的。而在验证系列第二篇中提到的 WAS 卖花网站中遇到的缺陷,我们需要传送新的 url 地址给 RFT 脚本。
b)在调用 RQM 命令行执行工具的时候就需要传输参数
清单 4. RQM 命令行调用附参数
c:\IBM\java60\bin\java -jar RQMExecutionTool.jar -tcerId=1 -projectName=QM1 -publicURI=https://paul801beta:9443/qm -user=paul -password=passw0rd -exitOnComplete=true -variables=host:clmsvr-sjy.cn.ibm.com |
c)RFT 中接受参数的脚本如下
清单 5. RFT 脚本
import com.rational.test.ft.script.IParameter; import com.rational.test.ft.script.IVariablesManager; public class SampleScript extends SampleScriptHelper { /** * Script Name : <b>SampleScript</b> * Generated : <b>Dec 10, 2012 1:44:05 PM</b> * Description : Functional Test Script * Original Host : WinNT Version 5.1 Build 2600 (S) * * @since 2012/12/10 * @author Administrator */ public void testMain(Object[] args) { //接收参数的定义 IVariablesManager manager = getVariablesManager(); IParameter host = manager.getInputParameter("host"); startApp("http://" + host.getValue() + ":9081/PlantsByWebSphere/"); // HTML Browser // Document: Plants by WebSphere: // http://clmsvr-sjy.cn.ibm.com:9081/PlantsByWebSphere/ // Document: http://clmsvr-sjy.cn.ibm.com:9081/PlantsByWebSphere/promo.html image_bonsaiTree().click(); // Document: // http://clmsvr-sjy.cn.ibm.com:9081/PlantsByWebSphere/servlet/ShoppingServlet? // action=productdetail&itemID=T0003 browser_htmlBrowser(document_plantsByWebSphere(),DEFAULT_FLAGS).inputChars("abc"); button_addToCart().click(); } } |
这里提示一下 RQM 命令行执行工具也提供了 ant 任务调用,方便把执行测试这部分集成到自动化的流程中。
实现部分 5:Jazz 插件的开发
Jazz 插件的开发与 Eclipse 插件开发模式是一致的,Jazz 平台定义了丰富而功能强大的扩展点,用户可以利用这些扩展点,定义和实现各种定制功能。
首先要搭建扩展开发环境,Jazz.net 网站提供了详细的下载 SDK 链接,以及参考文档。
RTC Server 端服务的扩展,有关 Jazz Component 开发详细资料请查看参考资源中提供的链接。Jazz Component 开发扩展扩展点"com.ibm.team.repository.common.components",定义服务类接口,类型是 Raw_HTTP。
清单 6
<?xml version="1.0" encoding="UTF-8"?> <?eclipse version="3.2"?> <plugin> <!-- This extension defines our component to Jazz. Note that the common plugin is included on both the client and server, so the component is known both places. --> <extension point="com.ibm.team.repository.common.components"> <component id="com.ibm.rational.svt.workitem.extensions" name="Workitem Validation Workflow"> <service kind="RAW_HTTP" name="Workitem Validation WorkFlow Rest Service" uri="com.ibm.rational.svt.workitem.extensions.common. IWorkitemValidationWorkflowRestService" version="1"> </service> </component> </extension> </plugin> |