qileilove

          blog已經轉移至github,大家請訪問 http://qaseven.github.io/

          敏捷開發和測試中重現缺陷和驗證缺陷的解決方案(3)

          簡介:在作為系列的最后一篇覆蓋的部分是缺陷生命周期的最后一個環節,缺陷的驗證。本文主要描述了如何通過 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>


          posted on 2013-04-12 11:43 順其自然EVO 閱讀(446) 評論(0)  編輯  收藏


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          <2013年4月>
          31123456
          78910111213
          14151617181920
          21222324252627
          2829301234
          567891011

          導航

          統計

          常用鏈接

          留言簿(55)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 百色市| 新源县| 云林县| 宁河县| 安达市| 宝应县| 九龙城区| 荃湾区| 德惠市| 宜城市| 肃宁县| 闵行区| 茌平县| 屏山县| 鹿邑县| 南华县| 元谋县| 永修县| 香河县| 嵩明县| 祥云县| 黄平县| 汉源县| 繁昌县| 永善县| 余干县| 贺兰县| 荔浦县| 牙克石市| 格尔木市| 洞口县| 西丰县| 铜川市| 绥棱县| 仪陇县| 宣威市| 堆龙德庆县| 施秉县| 珲春市| 鄱阳县| 濮阳县|