posted @ 2006-08-10 11:18 eddy liao 閱讀(371) | 評論 (1) | 編輯 收藏
Rational ClearCase是一個業界領先的軟件配置管理工具,Rational ClearQuest則是IBM Rational在變更管理和缺陷跟蹤方面的軟件。業界對于變更管理軟件和配置管理軟件的集成有著強烈的需求,因此IBM Rational也提供了ClearCase和ClearQuest集成的功能
Rational ClearCase是一個業界領先的軟件配置管理工具,Rational ClearQuest則是IBM Rational在變更管理和缺陷跟蹤方面的軟件。業界對于變更管理軟件和配置管理軟件的集成有著強烈的需求,因此IBM Rational也提供了ClearCase和ClearQuest集成的功能。
所謂Base ClearCase和ClearQuest的集成,就是將ClearQuest中的變更請求(Change Requeset)關聯到一個或多個ClearCase中元素(Element)的版本(Version)上。一個變更請求可以被關聯到一個或多個版本上,實施變更的這些版本的集合被稱作變更請求的變更集(Change Set)。一個版本可以被關聯到一個或多個變更請求,這些變更請求的集合被稱作版本的請求集(Request Set)。
集成對于不同的角色,有以下不同的功能:
一個項目經理指定在什么情況下需要讓用戶關聯版本到變更請求。也可以指定關聯變更請求的VOBs,branches,以及element types。
ClearQuest的管理員添加ClearCase的定義到ClearQuest的schema中。這使得變更請求可以顯示與它關聯的變更集。
使用ClearCase進行開發的人員,可以在Check Out或者Check In一個版本的時候,將這個版本關聯到一個或者更多的變更請求上。也可以查看一個變更請求的變更集。
在這篇文章中,我們將對Base ClearCase與ClearQuest集成的設計原理和運行環境的搭建與設置進行介紹,最后再提供一些操作范例。
所謂的Central Server就是將所有的腳本文件及配置文件放在一個目錄,當進行集成的時候,ClearCase就會在這個目錄中尋找配置文件(config.pl)、cqcc_launch腳本以及其他的代碼,而不是使用本地默認目錄的相應文件,因此提高了安全性和可維護性。與之對應的本地方式(Local Server)則是使用本地ClearCase目錄中的配置文件、腳本以及其他代碼。
就是將一個ClearCase操作中的所有與ClearQuest相關的操作,記錄到一個批處理文件中,ClearCase操作完成之后,再將這些操作一次性寫入到ClearQuest中。從而降低了登陸ClearQuest和在查詢ClearQuest的次數,大大的提高了性能。
批處理序列是將批處理的概念進一步擴展的產物。ClearCase認為所有進行的ClearCase都是在一個批處理當中,它記錄所有與ClearQuest相關的操作到批處理文件當中,以便在以后的某個時間完成與ClearQuest相關的操作。
就是在ClearCase的Check in完成之后,再進行ClearQuest的操作。一般的情況下,在ClearCase的Check in操作完成之后,才進行與ClearQuest相關的操作。這樣在Check in操作失敗的情況下,會造成ClearCase和ClearQuest的數據不一致。啟用此功能則可以避免這種錯誤。
就是在將變更請求關聯到某個版本的時候,不需要手工選擇,而是靠預先設置的請求ID或者根據ClearCase操作的注釋自動提取請求ID,來決定關聯的請求。
2.6 使用CQWeb方式的集成
在本地沒有安裝ClearQuest,或者不愿意使用本地的ClearQuest的情況下,可以使用CQWeb的方式使用CQWeb Server上的ClearQuest來實現ClearCase和ClearQuest的集成。
我們知道UCM是一種對版本控制的配制管理流程,而UCM是基于Base ClearCase的管理流程演變而來的。因此掌握并了解Base ClearCase的管理就顯得至關重要。Base ClearCase包含了一系列功能,它們能夠使開發人員做到并行開發,項目管理者也能通過制定相關的規則來使開發工作有序的進行。
在開發過程中,Base ClearCase應用"分支(Branch)"的方法來允許開發人員進行并行開發。任何在配制管理下的元素(Element),例如:文本文件,程序原代碼等,都會生成一個主分支,而主分支下還可以有多個下屬分支,它們的作用是用來支持在主分支上的開發。Base ClearCase 允許創建復雜的分支體系。在開發過程中,通過視圖(View)可以訪問特定元素集的特定版本,而這通過修改視圖的規則(Config Specification)就可以實現。UCM也使用"分支"的方法,但是這些分支不需要用手工來操作,而是通過"流(Stream)"來實現,通常情況下,一個項目存在一個集成流和多個開發流。
在項目管理方面,我們通過對項目的源文件打基線(Baseline)來呈現項目早期較穩定版本的雛形,并且基線可以用來連接一系列相關的源文件,比如像源代碼,測試計劃等等。UCM自動完成基線的創建,而Base ClearCase則通過對元素(Elements)的版本打標簽來創建基線。
通過以上對UCM和Base ClearCase的比較,因此在一個項目不是很大,并且業務流程相對簡單的情況下適合用Base ClearCase。
在Base CCCQ集成的過程中,運行環境的搭建尤為重要。
首先,需要在ClearCase客戶端和ClearCase注冊服務器安裝ClearCase。在ClearQuest Unix服務器和ClearQuest Windows服務器安裝ClearQuest。準備數據庫服務器。在ClearQuest Unix服務器上配置好DBSet,并添加User DB。之后就可以配置集成了。
集成的配置需要在ClearCase和ClearQuest上分別進行配置,才能完成。在ClearCase側,需要對VOB配置。當對一個VOB配置了集成之后,針對與這個VOB的ClearCase相關操作(例如CheckOut, CheckIn)都會激發腳本對ClearQuest數據庫的訪問,進而完成Base CC和CQ的集成。
在ClearQuest側,需要在數據庫中添加ClearCase的定義,只有加入了定義之后,數據庫中的請求的變更集才能夠顯示出來。
下面具體介紹配置過程。
4.2.1 將ClearCase package加入到一個ClearQuest DBset
由于ClearQuest schema包含了一些與多個ClearQuest user databases相關聯的特性,例如數據記錄的類型,區域,和形式。在開發人員將ClearCase中文件的版本與ClearQuest用戶數據庫中的變更請求相聯系的時候,必須將ClearCase的特性也加入到ClearQuest schema,此過程要在Windows端完成且過程如下所述:
- 開始 -> 程序 -> Rational Software -> Rational ClearQuest -> ClearQuest Designer
- 在ClearQuest Designer中,點擊Package -> Package Wizard
- 在安裝Package向導中,找到ClearCase 1.0和ClearCase Upgrade 1.0,如果這些Packages沒有列出,則點擊"More Packages",并將上述的兩個Packages添加到列表中。
- 選擇ClearCase 1.0 Package并點擊"下一步"
- 選擇一個將會應用ClearCase 1.0 Package的schema e.g. Defect Tracking,點擊"下一步"
- 選擇數據紀錄的類型并點擊"完成"
- 選擇File -> Check In來保存schema的最新版本
- 選擇Database -> Upgrade Database把schema的最新版本升級到ClearQuest user database中
4.2.2 在ClearCase VOBs上安裝觸發器(Triggers)
CCCQ的集成應用到了針對cleartool checkin, checkout和uncheckout操作的觸發器,觸發器的安裝與配制需要在Windows端配制,該Windows的Registry Server必須與UNIX上建VOBs的那臺Server指向同一臺Registry Server。具體配置過程如下所述:
4.2.2.1 同步UNIX與Windows上的ClearCase Regions
1) 在Windows上新建一個Region,名稱與需要同步的UNIX上的Region名稱相同,這時UNIX上的Region就在Registry Server上注冊了。
2) 運行 -> cleartool -> mkregion -tag <UNIX region>
3) 開始 -> 程序 -> Rational Software ->
4) Rational ClearCase'Administration'Region Synchronizer
5) 選擇需要同步的Windows Region和UNIX Region, 在Import Type一項上選擇"VOB Tags"并且選中"Show full storage directory paths.
6) 在"Unix VOB tags not found in the Windows region"列表中選擇需要引入的VOB,點擊"Import",這時"Create VOB Tag"對話框會顯示出來。在"Global Storage"一項中輸入在UNIX服務器上的VOB的網絡存儲路徑,并且在"Hostname"一項中輸入在Region內能夠解析的主機名。
4.2.2.2 將一個VOB安裝上Trigger
當一個VOB被引入(Import)后,我們可以對其安裝Trigger 在ClearCase中,點擊開始 -> 程序 -> Rational Software'Rational ClearCase'Administration'Integrations'ClearQuest Integration Configuration. 這時出現如下圖所示的對話框。
在"ClearCase - ClearQuest Integration Configuration"對話框中,我們可以看到所有在UNIX服務器端建立好的VOBs,并且可以對其中任何一個VOB安裝trigger。在這里,我們對VOB int4安裝Checkout和Checkin的trigger。Trigger的配制文件在config.pl中有詳細說明,關于trigger選擇的詳細內容可以參看上一章節。
提示:
- 觸發器使用config.pl配制文件來控制本地集成的配制參數。當選擇V2觸發器時,配置應用程序會將config.pl文件路徑設為CQCC/config.pl,在這個路徑中CQCC代表了本地的cc-home-dir/lib/perl5/CQCCTrigger/CQCC這個路徑,用戶可以根據需要將這個路徑改變為一個UNC路徑,因此所有的集成操作將調用一個中心配制文件config.pl。
- 在安裝觸發器時,只有VOB的所有者才可以對自己創建的VOB安裝觸發器。如果一個用戶e.g. Harry登陸Windows,他想對Andy在UNIX上創建的VOB安裝觸發器,這時會出現"無法得到觸發器類型"等警告。如果Harry希望可以對VOB安裝觸發器,那么需要執行以下兩步:
- 在DOS模式下運行Runas /user:RATIONALCC\Andy cmd.exe命令,這個命令將以Andy的身份打開一個DOS窗口,并提示輸入用戶名和密碼。
- 在驗證通過登陸后,另一個DOS窗口將會打開,在這個窗口中,運行"cqconfig"來以Andy的身份在VOB上安裝觸發器。
4.2.3 核心文件config.pl的配置
config.pl文件的配置在Base ClearCase與ClearQuest集成的操作中起到重要的作用。config.pl文件中包含了一系列變量及參數的設置,設置的描述,以及在哪里可以配制這些參數(是在config.pl文件本身中設置還是在系統環境變量中設置)。
config.pl文件在不同操作系統上的存儲路徑:
Windows:C:\Program Files\Rational\Clearcase\lib\perl5\CQCCTrigger\CQCC\config.pl
UNIX: /usr/atria/sun5/lib/perl/CQCCTrigger/CQCC/config.pl
下面就一些重要的參數配置進行詳細的說明:
4.2.3.1 定義用戶數據庫
&SetConfigParm("CQCC_DATABASE_ENTITY_LIST","SAMPL: defect");
CQCC_DATABASE_ENTITY_LIST參數定義了一個或多個數據庫和數據庫所支持的數據紀錄類型。當定義多個數據庫時,參數的使用格式為:dbname1: entity1,entity2; dbname2: entity3,entity4。值得注意的是數據紀錄類型必須為在schema中已定義好的內容。
4.2.3.2 定義DBsets
&SetConfigParm("CQCC_DATABASE_SET", "<db_set_name>");
在ClearQuest中,當建立有多個DBsets時,即有多個schema存儲空間時,CQCC_DATABASE_SET參數用來指定一個當前可以使用的schema存儲空間。
4.2.3.3 選擇集成模式: 文本模式或圖形模式
&SetConfigParm("CQCC_GUI_ENABLE", "OFF");
此參數是一個開啟Perl/TK GUI圖形界面的開關。如果設置為"ON"(默認情況下),那么圖形界面會在需要的情況下顯示,例如,在運行xclearcase時。如果設置為"Always",那么圖形界面會在命令行操作的形式也顯示。如果設置為"OFF",那么圖形界面將永遠不顯示,因此只可以用命令行操作。
4.2.3.4 開啟DEBUG模式
&SetConfigParm("CQCC_DEBUG", "1");
此參數用來控制在運行時模式下DEBUG報告的輸出級別。0 - 代表沒有輸出;1 - 代表基本輸出(針對高級別的操作);2 - 代表細節輸出。
提示:其他參數設置的詳細說明請參看config.pl文件。
4.2.4 執行Base CCCQ集成的最后檢驗
此時,根據以上所提供的信息,我們應能夠完成cqcc檢驗,檢驗ClearCase與ClearQuest是否能夠很有效的結合,并可以開始完成一些簡單的操作。
在UNIX客戶端運行:cqcc_launch -test
此時,cqcc_launch命令將會調用config.pl里的參數并試圖連接ClearQuest,如果連接成功,exit_status會顯示0,否則將顯示1(如下圖所示)
可以說,Base ClearCase的基本操作,就是Check Out和Check in兩個操作,下面就簡單介紹一下這兩個操作。
1) 在ClearCase Explorer中,選中一個文件,進行Check Out操作。如果是配置完成后第一次進行操作,需要輸入ClearQuest的用戶名和密碼。
2) 登陸成功后,就會出現QSW(Query Association Window)窗口,顯示滿足條件的缺陷。選擇缺陷,點擊Association按鈕,可以將其放到上側窗口中,點擊OK,即可完成關聯。
3) 關聯成功后,在ClearQuest中打開相應的缺陷,在ClearCase頁中,可以查看到關聯的文件。
4) 在ClearCase Explorer中右鍵點擊被關聯的文件,選擇版本屬性,查看被關聯的缺陷。
1) 在ClearCase Explorer中選中文件,進行Check Out操作,彈出QSW窗口。
2) 在ClearQuest中查看被關聯的文件。
3) 在ClearCase中查看被關聯的缺陷。
posted @ 2006-08-09 17:04 eddy liao 閱讀(414) | 評論 (0) | 編輯 收藏
1.在不同網絡環境中ClearCase的管理
http://www-128.ibm.com/developerworks/cn/rational/r-hanss/
2.IBM Rational ClearCase-Samba 協同環境的設置和問題解決
http://www-128.ibm.com/developerworks/cn/rational/r-cc-samba/
3.ClearCase Interoperation實例詳解
http://www-128.ibm.com/developerworks/cn/rational/06/r-shixl2/index.html
我也別貼了,到這里去找吧。
http://www-128.ibm.com/developerworks/cn/views/rational/articles.jsp
posted @ 2006-07-25 14:01 eddy liao 閱讀(328) | 評論 (0) | 編輯 收藏
http://www.microsoft.com/china/windowsserver2003/default.mspx
Windows Server 2003 部署通用結構分步指南
第一部分:將 Windows Server 2003 安裝為域控制器
http://www.microsoft.com/china/technet/prodtechnol/windowsserver2003/technologies/directory/activedirectory/stepbystep/domcntrl.mspx
?
Win2K安裝與服務器配置(下)
http://article.pchome.net/00/01/79/90/
Windows Server 2003中的Active Directory服務
http://www.microsoft.com/china/technet/community/columns/profwin/pw0503.mspx
輕松配置Windows2003自帶MAIL服務器
http://server.chinabyte.com/91/2394591.shtml
手把手教您架設Windows2003共享服務器
http://server.chinabyte.com/185/2482185.shtml
posted @ 2006-07-25 12:36 eddy liao 閱讀(9206) | 評論 (2) | 編輯 收藏
checkstyle 是一個幫助開發者按照某種習慣編寫 java 代碼的工具,他實現了代碼檢查的自動化,幫助人們從這種繁瑣的工作中解放出來。
默認提供了對 sun 編程規范的支持,但是 checkstyle 是一個具有高可配置性的,你完全可以根據自己的要求來配置需要檢查的內容。
有以下這些東西
\lib\checkstyle-3.1\contrib\checkstyle-noframes.xsl
\lib\checkstyle-3.1\checkstyle-all-3.1.jar
\lib\checkstyle-3.1\sun_checks.xml????????????????????
在build.xml文件中添加
??? <patternset id="java.files.pattern" includes="**/*.java"/>
??? <target name="checkstyle" depends="prepare"?????????? 依賴prepare target
??????? description="Check code style for compliance with coding standards">
??????? <property name="checkstyle.data.dir"
??????????? location="${build.dir}/docs/checkstyle"/>???????????????? 存放路徑
??????? <property name="checkstyle.data.file"
??????????? location="${checkstyle.data.dir}/checkstyle.xml"/>???? xml文件
??????? <property name="checkstyle.report.file"
??????????? location="${checkstyle.data.dir}/checkstyle.html"/>?? html文件
??????? <property name="checkstyle.xsl.file"
??????????? location="${checkstyle.dir}/contrib/checkstyle-noframes.xsl"/>?? 選用的樣式表,checkstyle.dir為jar包的位置
??????? <mkdir dir="${checkstyle.data.dir}"/>
??????? <taskdef resource="checkstyletask.properties" classpath="${checkstyle.jar}"/>? 引入jar文件
??????? <checkstyle config="${checkstyle.dir}/sun_checks.xml"????????????????????? 選用sun的規范,可以修改為自己的最佳實踐
??????????? failOnViolation="false" failureProperty="checkstyle.failure">
??????????? <fileset dir="src">?????????????????????????????????????????????????????????????????? 對src目錄進行檢查
??????????????? <patternset refid="java.files.pattern"/>
??????????? </fileset>
??????????? <fileset dir="test">????????????????????????????????????????????????????????????????? 對test目錄進行檢查
??????????????? <patternset refid="java.files.pattern"/>
??????????? </fileset>
??????????? <!-- uncomment to print to console as well -->
??????????? <!--formatter type="plain"/-->
??????????? <formatter type="xml" toFile="${checkstyle.data.file}"/>???????? 生成xml文件
??????? </checkstyle>
??????? <xslt in="${checkstyle.data.file}" out="${checkstyle.report.file}"
??????????? style="${checkstyle.xsl.file}"/>??????????????????????????????????????? 生成報告,其格式取決于checkstyle.xsl.file
??? </target>
如圖所示:圖1列出了所有文件,圖2列出了所以錯誤
下面解釋了一些常見的輸出結果,以供參考。 ?
序號 ? ? ? ? ? ?輸出內容意義 ? ?
1 ?Type ?is ?missing ?a ?javadoc ?commentClass ? ?缺少類型說明 ? ?
2“{” ?should ?be ?on ?the ?previous ?line ?“{” ?應該位于前一行 ? ?
3Methos ?is ?missing ?a ?javadoc ?comment方法前面缺少javadoc注釋 ? ?
4Expected ?@throws ?tag ?for ?“Exception”在注釋中希望有@throws的說明 ? ?
5“.” ?Is ?preceeded ?with ?whitespace ?“.” ?前面不能有空格 ? ?
6“.” ?Is ?followed ?by ?whitespace“.” ?后面不能有空格 ? ?
7“=” ?is ?not ?preceeded ?with ?whitespace“=” ?前面缺少空格 ? ?
8“=” ?is ?not ?followed ?with ?whitespace“=” ?后面缺少空格 ? ?
9“}” ?should ?be ?on ?the ?same ?line“}” ?應該與下條語句位于同一行 ? ?
10Unused ?@param ?tag ?for ?“unused”沒有參數“unused”,不需注釋 ? ?
11Variable ?“CA” ?missing ?javadoc變量“CA”缺少javadoc注釋 ? ?
12Line ?longer ?than ?80characters行長度超過80 ? ?
13Line ?contains ?a ?tab ?character行含有”tab” ?字符 ? ?
14Redundant ?“Public” ?modifier冗余的“public” ?modifier ? ?
15Final ?modifier ?out ?of ?order ?with ?the ?JSL ?suggestionFinal ?modifier的順序錯誤 ? ?
16Avoid ?using ?the ?“.*” ?form ?of ?importImport格式避免使用“.*” ? ?
17Redundant ?import ?from ?the ?same ?package從同一個包中Import內容 ? ?
18Unused ?import-java.util.listImport進來的java.util.list沒有被使用 ? ?
19Duplicate ?import ?to ?line ?13重復Import同一個內容 ? ?
20Import ?from ?illegal ?package從非法包中 ?Import內容 ? ?
21“while” ?construct ?must ?use ?“{}”“while” ?語句缺少“{}” ? ?
22Variable ?“sTest1” ?must ?be ?private ?and ?have ?accessor ?method變量“sTest1”應該是private的,并且有調用它的方法 ? ?
23Variable ?“ABC” ?must ?match ?pattern ?“^[a-z][a-zA-Z0-9]*$”變量“ABC”不符合命名規則“^[a-z][a-zA-Z0-9]*$” ? ?
24“(” ?is ?followed ?by ?whitespace“(” ?后面不能有空格 ?25“)” ?is ?proceeded ?by ?whitespace“)” ?前面不能有空格
25 Line has trailing spaces? 行的最后不能有空格
根據sun_checks.xml文件的內容,可以到http://checkstyle.sourceforge.net/checks.html這里查看具體的配置,實現你們的最佳實踐
posted @ 2006-07-20 15:51 eddy liao 閱讀(3042) | 評論 (0) | 編輯 收藏
用 Jester 對測試進行測試測試套件有缺陷,這不是玩笑 ![]() |
![]() |
![]() |
|
級別: 初級
Elliotte Rusty Harold
, 副教授, Polytechnic University
2005 年 6 月 02 日
全面的單元測試套件對健壯的程序是必不可少的。但是如何才能保證測試套件測試了應當測試的每件事呢?Ivan Moore 的 JUnit 測試的測試器 Jester,擅長發現測試套件的問題,并提供對代碼基本結構的深入觀察。Elliotte Rusty Harold 介紹了 Jester 并展示如何使用它才能得到最佳結果。
測試先行的開發是極限編程(XP)中爭議最少、采用最廣泛的部分。到目前為止,大多數專業 Java 程序員都可能捕捉過測試 bug。(請參閱 參考資料 獲得有關“被測試傳染”的更多信息。) JUnit 是 Java 社區事實上的標準測試框架,沒有經過全面的 JUnit 測試套件測試過的系統是不完整的。如果您的項目有全面的測試套件,那么恭喜您:您將制作出質量良好的、有利于工作的軟件。但是大多數代碼基礎相當復雜。您能確定每個方法都被測試到、每個分支都進入過么?如果不能,那么當這些方法和分支在生產中執行的時候,應用程序會如何表現呢?
對代碼進行測試的下一步是用 代碼覆蓋 工具對測試進行度量。代碼覆蓋是一種查看一套測試覆蓋了多少代碼的方法。信心的獲得,不僅需要知道測試了程序整體,還要知道每個方法在全部可能情況下都得到測試。傳統情況下,這類度量的執行方法是在測試執行時對測試進行監視,可以通過 Java 虛擬機調試接口(JVMDI)或 Java 虛擬機工具接口 (JVMTI)進行,或者直接處理字節碼。一次都沒有執行過的語句是測試不到的。
Clover 和 EMMA(參閱 參考資料) 這類工具采用的這種方法對于發現測試不到的語句很有價值 —— 但是還不夠。知道測試套件沒有執行某個語句,可以證明該語句沒測試到。但是,反過來不成立。如果執行了某一行代碼,并不一定代表它得到測試。完全有可能存在這樣的情況:測試并沒有檢查代碼行是否生成正確結果。
當然,沒有人會編寫測試套件對每個語句的結果都進行驗證。在眾多的問題當中,這個問題可能會破壞封裝。您可能認為,針對特定輸入,只有方法中的每一行都操作正確,方法才會生成預期結果。但是這個假設并不合理。例如,如果沒有測試到所有可能輸入,也就沒有測試到為處理邊際情況而設計的代碼,這時會如何呢?有可能還會測試到每行代碼,但有可能遺漏真正的 bug。
![]() |
|
![]() ![]() |
![]()
|
這正是 Jester 發揮作用的地方。與 Clover 這類傳統的代碼覆蓋工具不同,Jester 不去查看報告了哪行代碼。相反,Jester 會修改源代碼、重新編譯源代碼,然后運行測試套件,查看是否有什么事出錯。例如,它會把 1 改成 2,或者把 if (x > y)
改成 if (false)
。如果測試套件的關注不夠,沒有注意到修改,那么就說明遺漏了某項測試。
我將通過在開源的 Jaxen XPath 工具(參閱 參考資料)上應用 Jester 而對它進行演示。Jaxen 有一個基于 JUnit 的測試套件,而且這個套件的代碼覆蓋并不完善。
在運行 Jester 之前,所有對沒有修改的源代碼的單元測試都必須測試通過。如果不是這樣,那么 Jester 就無法知道是不是它的修改造成了破壞。(為了演示,我不得不修復一個 bug,我過去為它編寫了測試用例,但是沒有跟蹤修復它。)
Jester 與 IDE 的集成不是特別好(或者根本不好),所以要讓測試通過,重要的是正確設置 CLASSPATH
和目錄。運行測試套件所需要的命令行對于每個項目都是不同的。因為 Jaxen 測試使用指向特定測試文件的相對 URL ,所以它的測試必須在 jaxen 目錄中運行。下面是我最后運行 Jaxen 測試的方式:
|
在運行 Jester 之前,還需要清楚針對測試套件的一項附加限制。除非測試失敗,否則不能打印有關 System.err
的任何內容。Jester 要通過檢查打印的內容來判斷測試是否成功,所以對 System.err
的程序輸出會把 Jester 弄混。
測試套件運行無誤之后,請做一份源代碼樹的拷貝。記住,Jester 要向代碼故意加入 bug,所以您可不要冒險在出現問題的情況下遺漏一個 bug。(如果您在使用源代碼控制,那么這不會是個大問題。如果沒有,請暫停閱讀本文,立即把代碼簽入 CVS 或 Subversion 倉庫。)
要運行 Jester,在路徑中必須同時擁有 jester.jar 和 junit.jar(JUnit 沒有和 Jester 綁在一起。需要分別下載)。Jester 在類路徑中查找它的配置文件,所以必須還要把 Jester 的主目錄放在類路徑中。當然,還需要添加所測試的應用程序需要的其他 JAR。主類是 jester.TestTester
。傳遞給這個程序的參數是測試應用程序的測試套件名稱。(我不得不為 Jaxen 編寫一個主類,因為它沒有包含一個可以運行它的全部測試的類。)如果把全部必要的 JAR 文件和目錄都添加到 CLASSPATH
環境變量,而不是把它們添加到 jre/lib/ext 或者用 -classpath
引用它們,那么 Jester 工作起來會更加穩定。下面是我針對 Jaxen 運行初始測試的方式:
|
Jester 運行很慢,即使檢測一個文件也是如此。它顯示一個進度對話框,如圖 1 所示,并在 System.out
上打印輸出,讓您知道它在做的工作,并向您保證它并沒有完全掛起。
圖 1. Jester 進度

如果在第一次運行若干分鐘(或者時間足夠運行完整的測試套件,甚至更長)之后,什么輸出也沒有看到,那么 Jester 可能 確實 掛起了,這很可能是因為類路徑的問題。如果每件事都進行順利,那么應當看到像清單 1 所示的輸出:
清單 1. Jester 輸出
|
從清單 1 中可以看到,BaseXPath
沒有得到很好的測試。Jester 對類進行了 11 項修改,而只有一項造成測試失敗。有些修改是假陽性,但是 11 處修改肯定不應當只報告 1 處。
下一步是在不破壞測試套件的情況下查看 Jester 改變的代碼,看看是否需要為它編寫測試。Jester 在 GUI 中顯示它進行的修改,如 圖 1 所示(它不能在無人控制的情況下運行,這有點煩人),在控制臺上打印輸出,如 清單 1 所示,并生成 XML 文件,文件中是沒有產生影響的修改列表,如清單 2 所示:
清單 2. Jester XML 輸出
|
Jester 的行號報告通常不是個好方法,所以最好是在控制臺輸出中查找修改的代碼。下面是 清單 1 的報告中的修改:
|
在這個方法中,這個修改是在類的結束處:
|
對測試套件迅速查找之后發現,實際上沒有測試調用 selectSingleNodeForContext
。所以下一步就是為這個方法編寫一個測試。這個方法是 protected 的方法,所以測試不能直接調用它。有時需要編寫一個子類(通常作為內部類)來測試 protected 的方法。但是在這個例子中,稍做一點檢查就很快發現這個方法由同一個類中的兩個 public 方法(stringValue
和 numberValue
)直接調用。所以也可以用這兩個方法來測試它:
|
最后一步是運行測試用例,確定它通過。下面是結果:
|
Jester 捕捉到一個 bug!方法沒有像預期的那樣工作。更有趣的是,對 bug 的調查揭示出潛在的設計缺陷。BaseXPath
類可能更適合作為抽象類而不是具體類。我發誓,我并不是特意挑選這個示例來公開這個 bug。我從 BaseXPath
開始只是因為它是頂級 org.jaxen 包的第一個類,而且我選擇 selectSingleNodeForContext
作為所測試的方法也只是因為它是 Jester 報告的最后一個錯誤。我真的認為這個方法沒有什么問題,但是我錯了。如果某些事沒有經過測試,那么就應當假設它是有問題的。Jester 會告訴您出了什么問題。
下一步顯而易見:修復 bug。(請確保同時對 Jester 正在處理的源樹拷貝和實際樹中的 bug 進行了修復。)然后,迭代 —— 針對這個類重新運行 Jester,直到任何修改都不能通過,或者可以通過的修改都是不相關的。在我為這個 bug 添加測試(并修復)之后,Jester 就報告 11 個修改中只有 8 個沒有檢測到,如 清單 2 所示。這在調試中是經常出現的事:修復了一個問題就修復(或者暴露了)另外幾個。
![]() ![]() |
![]()
|
因為 Jester 重新編譯代碼基,而且要為自己做的每個修改都重新運行測試套件,所以它的運行要比 Clover 這樣的傳統工具慢得多。因此,對性能加以關注是很重要的??梢杂迷S多技術加快 Jester 的運行。
首先,如果編譯在 Jester 執行時間中占了顯著部分,那么請嘗試使用一個更快的編譯器。許多用戶都報告采用 Jikes 代替 Javac 后速度有顯著提高(參閱 參考資料)。可以在 Jester 主目錄中的 jester.cfg 文件中修改 Jester 使用的編譯命令。
第二,剖析和優化測試套件。一般情況下,人們對單元測試運行的速度沒太注意,但是如果乘上 Jester 上千次執行測試套件的次數,那么任何節約都會非常顯著。具體來說,要在測試套件中查找在正常代碼中不會出現的問題。JUnit 會重新初始化每個執行方法的全部字段,所以如果不是測試類的每個方法都用的字段,那么把測試數據從字段中拿出來放在本地變量中,可以顯著提高速度。如果形成的代碼副本不合您的風格,請嘗試把測試套件分成更小、更模塊化的類,以便所有的初始數據可以在全部測試方法之間共享。
第三,重新組織測試套件的 suite
方法,以便最脆弱的測試(修改之后最有可能出錯的)在不太脆弱的測試之前運行。只要 Jester 發現一個測試失敗,就會終止運行,所以盡早失敗可以短路大量耗時的額外測試。
第四,出于相似的原因,當測試失敗的機會差不多時,把最快的測試放在第一位。按照大概的執行時間給測試排序。只在內存中執行的測試在訪問磁盤的測試之前,訪問磁盤的測試在訪問 LAN 的測試之前,訪問 LAN 的測試在訪問 Internet 的測試之前。如果有些測試特別慢,試試去掉它們,即便這會增加假陽性的數量。在 XOM (一個用 Java 語言處理 XML 的 API)的測試套件中,在 50 個測試類中,只有很少的幾個就占據了 90% 以上的執行時間。在測試的時候清除這些類可以帶來 10 倍的性能提升。
最后,也是最重要的,就是不要一次測試整個代碼基。每次把測試限制在一個類上,而且只運行能夠暴露這個類的覆蓋不足的測試??赡苄枰L時間來測試每個類,但是用這種方法,幾乎可以立即填補不足、修復 bug,而不必為 Jester 的一次運行完成等上好幾天。
![]() ![]() |
![]()
|
Jester 是聰明的程序員的工具包中一個重要的附加。它可以發現其他工具不能發現的代碼覆蓋不足,這會直接變成發現和修復 bug。使用 Jester 對代碼基進行測試,可以制造出更強壯的軟件。
![]() ![]() |
![]()
|
- 您可以參閱本文在 developerWorks 全球站點上的 英文原文。
- 從 SourceForge 下載 Jester。
- 得到 test infected。
-
JUnit
是 Java 代碼事實上的標準單元測試框架,也是 Jester 依賴的框架。
- 在本文中作為示例的 Jaxen 項目是一個用于 Java 語言的開源 XPath 引擎,適用于許多不同的對象模型。
-
Clover
是一個更傳統的測試覆蓋工具,是 Jester 的有益補充。它比 Jester 更容易使用,也快得多;但是它只能測試在測試期間執行的代碼,而不是它真正要測試的代碼。
-
EMMA
是一個免費、開源的代碼覆蓋工具。請學習面向 Java 程序員的各種不同的 開源單元測試工具 和 開源代碼覆蓋工具 的更多內容。
- 請閱讀 Dave Thomas 和 Andy Hunt 的 Pragmatic Unit Testing in Java With JUnit(Pragmatic Bookshelf, 2003)。
- Dennis M. Sosnoski 通過介紹開源的 Hansel 和 Gretel 代碼覆蓋工具,開始 了 Classworking 工具箱 系列。
- David Carew 和 Sandeep Desai 撰寫的“Keeping critters out of your code: How to use WebSphere and JUnit to prevent programming bugs”(developerWorks, 2003 年 6 月),介紹了采用 XP 方法進行測試。
- Malcolm Davis 撰寫的“利用 Ant 和 JUnit 進行增量開發”(developerWorks,2000 年 11 月),解釋了如何把 JUnit 集成到自己的項目中。
- Eric Allen 和 Roy Miller 在他們各自的專欄 診斷 Java 代碼 和 揭開極端編程的神秘面紗 中頻繁地涉及到單元測試。
- Erik Hatcher 撰寫的“讓編譯和測試過程自動化”(developerWorks,2001 年 8 月),介紹了如何把增量測試和持續構建組合到一個自動的過程中。
-
Testdriven.com
是一個關于測試驅動開發的文章和資源的全面集合。
-
Jikes
是一個用 C 編寫的開源的 Java 編譯器,運行起來比 javac 快得多。
- 在 developerWorks Java 技術專區 可以找到 Java 編程各方面的文章。
- 請訪問 Developer Bookstore,獲得技術書籍的完整列表,其中包括數百本 Java 相關主題 的圖書。
- 通過參與 developerWorks blogs 加入 developerWorks 社區。
![]() ![]() |
![]()
|
![]() |
||
|
![]() |
Elliotte Rusty Harold 來自新奧爾良,現在他還定期返回新奧爾良研究一盆秋葵。但是,他和妻子 Beth 及他們的貓咪 Charm(以夸克命名)和 Marjorie(以他的岳母為名),定居在布魯克林附近的 Prospect Heights。他是 Polytechnic 大學計算機科學的副教授,他在該校講授 Java 和面向對象編程。他的 Web 站點 Cafe au Lait 已經成為 Internet 上最流行的獨立 Java 站點之一,他的分站點 Cafe con Leche 已經成為最流行的 XML 站點之一。他的書包括 Effective XML、Processing XML with Java、Java Network Programming和 The XML 1.1 Bible。他目前在開發處理 XML 的 XOM API 和 XQuisitor GUI 查詢工具。 |
posted @ 2006-07-19 15:33 eddy liao 閱讀(387) | 評論 (0) | 編輯 收藏
Quilt
Quilt is a Java software development tool that measures coverage , the extent to which unit testing exercises the software under test. It is optimized for use with the JUnit unit test package, the Ant Java build facility, and the Maven project management toolkit. |
EMMA
EMMA is an open-source toolkit for measuring and reporting Java code coverage. EMMA distinguishes itself from other tools by going after a unique feature combination: support for large-scale enterprise software development while keeping individual developer's work fast and iterative at the same time. Every developer on your team can now get code coverage for free and they can get it fast! EMMA is so lightweight developers can use it during the process of writing tests instead of waiting for a "test build". This gets code coverage where it belongs: helping with design and implementation before the code is checked in. |
NoUnit
NoUnit allows you to see how good your JUnit tests are. It generates a report from your code to graphically show you how many of your project's methods are being tested , and how well. |
InsECT
InsECT which stands for Instrumentation Execution Coverage Tool, is a system developed in Java to obtain coverage information for Java programs. InsECT instruments (inserts instructions into) Java class files at the bytecode level with probes to report information about a system at runtime. The goal of InsECT is to provide detailed coverage information about Java programs by taking into full account the object-oriented behavior and language features of Java. Furthermore, as an open-source project, InsECT is designed to be extensible for use in a wide variety of dynamic analyses. InsECT utilizes the Byte Code Engineering Library. |
Hansel
Hansel is an extension to JUnit that adds code coverage testing to the testing framework. |
Jester
Jester finds code that is not covered by tests. Jester makes some change to your code, runs your tests, and if the tests pass Jester displays a message saying what it changed. Jester includes a script for generating web pages that show the changes made that did not cause the tests to fail. |
JVMDI Code Coverage Analyser
This small utility is a shared library which when loaded into a Java VM (1.4+) which supports JVMDI will record all the lines of code executed. This is a relatively coarse coverage method, but good enough for a lot of purposes. |
Go To JVMDI Code Coverage Analyser
GroboCodeCoverage
GroboCodeCoverage is a 100% Pure Java implementation of a Code Coverage tool. It uses Jakarta's BCEL platform to post-compile class files to add logging statements for tracking coverage. |
jcoverage/gpl
jcoverage/gpl is a free code-coverage tool for Java聶 programmers that allows them to measure the effectiveness of their Java tests and how much of a software program's code has been tested. jcoverage/gpl identifies how many times each line of code in your application has been executed and you can see which parts of your software remain untested. After instrumenting your code and running your tests, a report is generated allowing you to view information coverage figures from a project level right down to the individual line of code. This process is called 'code coverage'. |
JBlanket
JBlanket is a tool for assessing and improving method coverage of unit test cases. It is integrated with JUnit and Ant. |
Cobertura
Cobertura is a free Java tool that calculates the percentage of code accessed by tests. It can be used to identify which parts of your Java program are lacking test coverage. It is based on jcoverage. Cobertura produces very nice reports. It works by instrumenting Java bytecode after it has been compiled. |
Coverlipse
An eclipse plugin for code coverage visualization of JUnit Tests. Supported coverages include block coverage and all-uses coverage (Data Flow Analysis). License is CPL (Common Public License) |
posted @ 2006-07-19 15:29 eddy liao 閱讀(927) | 評論 (0) | 編輯 收藏
追求代碼質量: 監視圈復雜度當代碼復雜度超出想像時該如何做 ![]() |
![]() |
![]() |
|
級別: 初級
Andrew Glover
, 總裁, Stelligent Incorporated
2006 年 4 月 25 日
如果復雜度與缺陷緊密相關,那么監視代碼庫的復雜度值不是很有意義嗎?Andrew Glover 將展示如何使用簡單的代碼度量工具和基于 Java? 的工具來監視圈復雜度 (cyclomatic complexity)。
每位開發人員對代碼質量的含義都有著自己的看法,并且大多數人對如何查找編寫欠佳的代碼也有自己的想法。甚至術語代碼味道(code smell) 也已進入大眾詞匯表,成為描述代碼需要改進的一種方式。
![]() |
|
代碼味道通常由開發人員直接判定,有趣的是,它是許多代碼注釋綜合在一起的味道。一些人聲稱公正的代碼注釋是好事情,而另一些人聲稱代碼注釋只是解釋過于復雜的代碼的一種機制。顯然,Javadocs? 很有用,但是多少內嵌注釋才足以維護代碼?如果代碼已經編寫得足夠好,它還需要解釋自己嗎?
這告訴我們,代碼味道是一種評估代碼的機制,它具有主觀性。我相信,那些聞起來味道糟透了的代碼可能是其他人曾經編寫的最好的代碼。以下這些短語聽起來是不是很熟悉?
是的,它初看起來有點亂,但是您要看到它多么可擴展?。?/i>
或者
它讓您感到迷惑,但顯然您不了解它的模式。
我們需要的是客觀評估代碼質量的方法,某種可以決定性地告訴我們正在查看的代碼是否存在風險的東西。不管您是否相信,這種東西確實存在!用來客觀評估代碼質量的機制已經出現了一段時間了,只是大多數開發人員忽略了它們。這些機制被稱為代碼度量 (code metric)。
幾十年前,少數幾個非常聰明的人開始研究代碼,希望定義一個能夠與缺陷關聯的測量系統。這是一個非常有趣的主張:通過研究帶 bug 代碼中的模式,他們希望創建正式的模型,然后可以評估這些模型,在缺陷成為缺陷之前 捕獲它們。
在這條研究之路上,其他一些非常聰明的人也決定通過研究代碼看看他們是否可以測量開發人員的生產效率。對每位開發人員的代碼行的經典度量似乎只停留在表面上:
Joe 生產的代碼要比 Bill 多,因此 Joe 生產率更高一些,值得我們花錢聘請這樣的人。此外,我注意到 Bill 經常在飲水機邊閑晃,我認為我們應該解雇 Bill。
但是這種生產率度量在實踐中是非常令人失望的,主要是因為它容易被濫用。一些代碼測量包括內嵌注釋,并且這種度量實際上受益于剪切粘貼式開發 (cut-and-paste style development)。
Joe 編寫了許多缺陷!其他每條缺陷也都是由他間接造成的。我們不該解雇 Bill,他的代碼實際上是免檢的。
可以預見,生產率研究被證實是非常不準確的,但在管理團隊 (management body) 廣泛使用這種生產率度量以期了解每個人的能力的價值之前,情況并非如此。來自開發人員社區的痛苦反應是有理由的,對于一些人而言,那種痛苦感覺從未真正走遠。
盡管存在這些失敗,但在那些復雜度與缺陷的相互關系的研究中仍然有一些美玉。大多數開發人員忘記進行代碼質量研究已有很長一段時間了,但對于那些仍正在鉆研的人而言(特別是如果您也正在為追求代碼質量而努力鉆研),會在今天的應用中發現這些研究的價值。例如,您曾注意到一些長的方法有時難以理解嗎?是否曾無法理解嵌套很深的條件從句中的邏輯?您的避開這類代碼的本能是正確的。一些長的方法和帶有大量路徑的方法是 難以理解的,有趣的是,這類方法容易導致缺陷。
我將使用一些例子展示我要表達的意思。
![]() ![]() |
![]()
|
研究顯示,平均每人在其大腦中大約能夠處理 7(±2)位數字。這就是為什么大多數人可以很容易地記住電話號碼,但卻很難記住大于 7 位數字的信用卡號碼、發射次序和其他數字序列的原因。
此原理還可以應用于代碼的理解上。您以前大概已經看到過類似清單 1 中所示的代碼片段:
清單 1. 適用記憶數字的原理
|
清單 1 展示了 9 條不同的路徑。該代碼片段實際上是一個 350 多行的方法的一部分,該方法展示了 41 條不同的路徑。設想一下,如果您被分配一項任務,要修改此方法以添加一項新功能。如果您該方法不是您編寫的,您認為您能只做必要的更改而不會引入任何缺陷嗎?
當然,您應該編寫一個測試用例,但您會認為該測試用例能將您的特定更改在條件從句的海洋中隔離起來嗎?
![]() ![]() |
![]()
|
圈復雜度 是在我前面提到的那些研究期間開創的,它可以精確地測量路徑復雜度。通過利用某一方法路由不同的路徑,這一基于整數的度量可適當地描述方法復雜度。實際上,過去幾年的各種研究已經確定:圈復雜度(或 CC)大于 10 的方法存在很大的出錯風險。因為 CC 通過某一方法來表示路徑,這是用來確定某一方法到達 100% 的覆蓋率將需要多少測試用例的一個好方法。例如,以下代碼(您可能記得本系列的第一篇文章中使用過它)包含一個邏輯缺陷:
清單 2. PathCoverage 有一個缺陷!
|
作為響應,我可以編寫一個測試,它將達到 100% 的行覆蓋率:
清單 3. 一個測試產生完全覆蓋!
|
接下來,我將運行一個代碼覆蓋率工具,比如 Cobertura,并將獲得如圖 1 中所示的報告:
圖 1. Cobertura 報告

哦,有點失望。代碼覆蓋率報告指示 100% 的覆蓋率,但我們知道這是一個誤導。
注意,清單 2 中的 pathExample()
方法有一個值為 2 的 CC(一個用于默認路徑,一個用于 if
路徑)。使用 CC 作為更精確的覆蓋率測量尺度意味著第二個測試用例是必需的。在這里,它將是不進入 if
條件語句而采用的路徑,如清單 4 中的 testPathExampleFalse()
方法所示:
清單 4. 沿著較少采用的路徑向下
|
正如您可以看到的,運行這個新測試用例會產生一個令人討厭的 NullPointerException
。在這里,有趣的是我們可以使用圈復雜度而不是 使用代碼覆蓋率來找出這個缺陷。代碼覆蓋率指示我們已經在一個測試用例之后完成了此操作,但 CC 卻會強迫我們編寫額外的測試用例。不算太壞,是吧?
幸運的是,這里的測試中的方法有一個值為 2 的 CC。設想一下該缺陷被隱藏在 CC 為 102 的方法中的情況。祝您好運找到它!
![]() ![]() |
![]()
|
Java 開發人員可使用一些開放源碼工具來報告圈復雜度。其中一個這樣的工具是 JavaNCSS,它通過檢查 Java 源文件來確定方法和類的長度。此外,此工具還收集代碼庫中每個方法的圈復雜度。通過利用 Ant 任務或 Maven 插件配置 JavaNCSS,可以生成一個列出以下內容的 XML 報告:
- 每個包中的類、方法、非注釋代碼行和各種注釋樣式的總數。
- 每個類中非注釋代碼行、方法、內部類和 Javadoc 注釋的總數。
- 代碼庫中每個方法的非注釋代碼行的總數和圈復雜度。
該工具附帶了少量樣式表,可以使用它們來生成總結數據的 HTML 報告。例如,圖 2 闡述了 Maven 生成的報告:
圖 2. Maven 生成的 JavaNCSS 報告

此報告中帶有 Top 30 functions containing the most NCSS 標簽的部分詳細描述了代碼庫中最長的方法,順便提一句,該方法幾乎總是 與包含最大圈復雜度的方法相關聯。例如,該報告列出了 DBInsertQueue
類的 updatePCensus()
方法,因為此方法的非注釋行總數為 283,圈復雜度(標記為 CCN)為 114。
正如上面所演示的,圈復雜度是代碼復雜度的一個好的指示器;此外,它還是用于開發人員測試的一個極好的衡量器。一個好的經驗法則是創建數量與將被測試代碼的圈復雜度值相等的測試用例。在圖 2 中所見的 updatePCensus()
方法中,將需要 114 個測試用例來達到完全覆蓋。
![]() ![]() |
![]()
|
在面對指示高圈復雜度值的報告時,第一個行動是檢驗所有相應測試的存在。如果存在一些測試,測試的數量是多少?除了極少數代碼庫以外,幾乎所有代碼庫實際上都有 114 個測試用例用于 updatePCensus()
方法(實際上,為一個方法編寫如此多的測試用例可能會花費很長時間)。但即使是很小的一點進步,它也是減少方法中存在缺陷風險的一個偉大開始。
如果沒有任何相關的測試用例,顯然需要測試該方法。您首先想到的可能是:到重構的時間了,但這樣做將打破第一個重構規則,即將編寫一個測試用例。先編寫測試用例會降低重構中的風險。減少圈復雜度的最有效方式是隔離代碼部分,將它們放入新的方法中。這會降低復雜度,使方法更容易管理(因此更容易測試)。當然,隨后應該測試那些更小的方法。
在持續集成環境中,隨時間變化 評估方法的復雜度是有可能的。如果是第一次運行報告,那么您可以監視方法的復雜度值或任何相關的成長度(growth)。如果在 CC 中看到一個成長度,那么您可以采取適當的動作。
如果某一方法的 CC 值在不斷增長,那么您有兩個響應選擇:
- 確保相關測試的健康情況仍然表現為減少風險。
- 評估重構方法減少任何長期維護問題的可能性。
還要注意的是,JavaNCSS 不是惟一用于 Java 平臺促進復雜度報告的工具。PMD 是另一個分析 Java 源文件的開源項目,它有一系列的規則,其中之一就是報告圈復雜度。CheckStyle 是另一個具有類似的圈復雜度規則的開放源碼項目。PMD 和 CheckStyle 都有 Ant 任務和 Maven 插件(請參閱 參考資料,從那里獲得關于至此為止討論的所有工具的更多信息。)
![]() ![]() |
![]()
|
因為圈復雜度是如此好的一個代碼復雜度指示器,所以測試驅動的開發 (test-driven development) 和低 CC 值之間存在著緊密相關的聯系。在編寫測試時(注意,我沒有暗示是第一次),開發人員通常傾向于編寫不太復雜的代碼,因為復雜的代碼難以測試。如果您發現自己難以編寫某一代碼,那么這是一種警示,表示正在測試的代碼可能很復雜。在這些情況下,TDD 的簡短的 “代碼、測試、代碼、測試” 循環將導致重構,而這將繼續驅使非復雜代碼的開發。
所以,在使用遺留代碼庫的情況下,測量圈復雜度特別有價值。此外,它有助于分布式開發團隊監視 CC 值,甚至對具有各種技術級別的大型團隊也是如此。確定代碼庫中類方法的 CC 并連續監視這些值將使您的團隊在復雜問題出現時 搶先處理它們。
![]() ![]() |
![]()
|
學習
- 您可以參閱本文在 developerWorks 全球站點上的 英文原文 。
- “追求代碼質量: 不要被覆蓋報告所迷惑”(Andrew Glover,developerWorks,2006 年 1 月):測試覆蓋率的測量是否讓您誤入歧途?找出新系列中的第一篇文章!
- “測試優先 Ruby 編程”(Pat Eyler,developerWorks,2005 年 5 月):test-first 開發中的一個簡單練習,其中包括關于重構的討論。
- “用 Cobertura 測量測試覆蓋率”(Elliotte Rusty Harold,developerWorks,2005 年 5 月):關于使用 Cobertura 來測量測試覆蓋率的初級讀物。
- Java 技術專區 :數百篇 Java 編程各方面的文章。
獲得產品和技術
-
JavaNCSS
:適用于 Java 平臺的一個源代碼測量套件。
-
PMD
:這個流行的開放源碼工具掃描 Java 代碼以發現問題。
-
CheckStyle
:來自 SourceForge 的另一個 Java 分析工具。
討論
-
參與論壇討論
。
-
developerWorks blogs
:加入 developerWorks 社區!
![]() ![]() |
![]()
|
![]() |
||
|
![]() |
Andrew Glover 是 Stelligent Incorporated 的總裁,該公司采用有效的開發人員測試策略和持續集成技術(讓團隊能夠盡早且經常地監視代碼質量)幫助其他公司解決軟件開發質量問題。他還是 Java Testing Patterns(Wiley,2004 年 9 月)一書的合著者。 |
posted @ 2006-07-19 15:06 eddy liao 閱讀(414) | 評論 (0) | 編輯 收藏
用 Cobertura 測量測試覆蓋率找出隱藏 bug 的未測試到的代碼 ![]() |
![]() |
![]() |
|
級別: 初級
Elliotte Rusty Harold
, 副教授, Polytechnic University
2005 年 5 月 26 日
Cobertura 是一種開源工具,它通過檢測基本的代碼,并觀察在測試包運行時執行了哪些代碼和沒有執行哪些代碼,來測量測試覆蓋率。除了找出未測試到的代碼并發現 bug 外,Cobertura 還可以通過標記無用的、執行不到的代碼來優化代碼,還可以提供 API 實際操作的內部信息。Elliotte Rusty Harold 將與您分享如何利用代碼覆蓋率的最佳實踐來使用 Cobertura。
盡管測試先行編程(test-first programming)和單元測試已不能算是新概念,但測試驅動的開發仍然是過去 10 年中最重要的編程創新。最好的一些編程人員在過去半個世紀中一直在使用這些技術,不過,只是在最近幾年,這些技術才被廣泛地視為在時間及成本預算內開發健壯的無缺陷軟件的關鍵所在。但是,測試驅動的開發不能超過測試所能達到的程度。測試改進了代碼質量,但這也只是針對實際測試到的那部分代碼而言的。您需要有一個工具告訴您程序的哪些部分沒有測試到,這樣就可以針對這些部分編寫測試代碼并找出更多 bug。
Mark Doliner 的 Cobertura (cobertura 在西班牙語是覆蓋的意思)是完成這項任務的一個免費 GPL 工具。Cobertura 通過用額外的語句記錄在執行測試包時,哪些行被測試到、哪些行沒有被測試到,通過這種方式來度量字節碼,以便對測試進行監視。然后它生成一個 HTML 或者 XML 格式的報告,指出代碼中的哪些包、哪些類、哪些方法和哪些行沒有測試到??梢葬槍@些特定的區域編寫更多的測試代碼,以發現所有隱藏的 bug。
我們首先查看生成的 Cobertura 輸出。圖 1 顯示了對 Jaxen 測試包運行 Cobertura 生成的報告(請參閱 參考資料)。從該報告中,可以看到從很好(在 org.jaxen.expr.iter
包中幾乎是 100%)到極差(在 org.jaxen.dom.html
中完全沒有覆蓋)的覆蓋率結果。
圖 1. Jaxen 的包級別覆蓋率統計數據

Cobertura 通過被測試的行數和被測試的分支數來計算覆蓋率。第一次測試時,兩種測試方法之間的差別并不是很重要。Cobertura 還為類計算平均 McCabe 復雜度(請參閱 參考資料)。
可以深入挖掘 HTML 報告,了解特定包或者類的覆蓋率。圖 2 顯示了 org.jaxen.function
包的覆蓋率統計。在這個包中,覆蓋率的范圍從 SumFunction
類的 100% 到 IdFunction
類的僅為 5%。
圖 2. org.jaxen.function 包中的代碼覆蓋率

進一步深入到單獨的類中,具體查看哪一行代碼沒有測試到。圖 3 顯示了 NameFunction
類中的部分覆蓋率。最左邊一欄顯示行號。后一欄顯示了執行測試時這一行被執行的次數??梢钥闯觯?112 行被執行了 100 次,第 114 行被執行了 28 次。用紅色突出顯示的那些行則根本沒有測試到。這個報告表明,雖然從總體上說該方法被測試到了,但實際上還有許多分支沒有測試到。
圖 3. NameFunction 類中的代碼覆蓋率

![]() |
|
利用 Cobertura 報告,可以找出代碼中未測試的部分并針對它們編寫測試。例如,圖 3 顯示 Jaxen 需要進行一些測試,運用 name()
函數對文字節點、注釋節點、處理指令節點、屬性節點和名稱空間節點進行測試。
如果有許多未覆蓋的代碼,像 Cobertura 在這里報告的那樣,那么添加所有缺少的測試將會非常耗時,但也是值得的。不一定要一次完成它。您可以從被測試的最少的代碼開始,比如那些所有沒有覆蓋的包。在測試所有的包之后,就可以對每一個顯示為沒有覆蓋的類編寫一些測試代碼。對所有類進行專門測試后,還要為所有未覆蓋的方法編寫測試代碼。在測試所有方法之后,就可以開始分析對未測試的語句進行測試的必要性。
是否有一些可以測試但不應測試的內容?這取決于您問的是誰。在 JUnit FAQ 中,J. B. Rainsberger 寫到“一般的看法是:如果 自身 不會出問題,那么它會因為太簡單而不會出問題。第一個例子是 getX()
方法。假定 getX()
方法只提供某一實例變量的值。在這種情況下,除非編譯器或者解釋器出了問題,否則 getX()
是不會出問題的。因此,不用測試 getX()
,測試它不會帶來任何好處。對于 setX()
方法來說也是如此,不過,如果 setX()
方法確實要進行任何參數驗證,或者說確實有副作用,那么還是有必要對其進行測試?!?
![]() |
|
我不同意。我已經記不清在“簡單得不會出問題”的代碼中發現的 bug 的數量了。確實,一些 getter 和 setter 很簡單,不可能出問題。但是我從來就沒有辦法區分哪些方法是真的簡單得不會出錯,哪些方法只是看上去如此。編寫覆蓋像 setter 和 getter 這樣簡單方法的測試代碼并不難。為此所花的少量時間會因為在這些方法中發現未曾預料到的 bug 而得到補償。
一般來說,開始測量后,達到 90% 的測試覆蓋率是很容易的。將覆蓋率提高到 95% 或者更高就需要動一下腦筋。例如,可能需要裝載不同版本的支持庫,以測試沒有在所有版本的庫中出現的 bug。或者需要重新構建代碼,以便測試通常執行不到的部分代碼??梢詫︻愡M行擴展,讓它們的受保護方法變為公共方法,這樣就可以對這些方法進行測試。這些技巧看起來像是多此一舉,但是它們曾幫助我在一半的時間內發現更多的未發現的 bug。
并不總是可以得到完美的、100% 的代碼覆蓋率。有時您會發現,不管對代碼如何改造,仍然有一些行、方法、甚至是整個類是測試不到的。下面是您可能會遇到的挑戰的一些例子:
- 只在特定平臺上執行的代碼。例如,在一個設計良好的 GUI 應用程序中,添加一個 Exit 菜單項的代碼可以在 Windows PC 上運行,但它不能在 Mac 機上運行。
- 捕獲不會發生的異常的
catch
語句,比如在從ByteArrayInputStream
進行讀取操作時拋出的IOException
。 - 非公共類中的一些方法,它們永遠也不會被實際調用,只是為了滿足某個接口契約而必須實現。
- 處理虛擬機 bug 的代碼塊,比如說,不能識別 UTF-8 編碼。
考慮到上面這些以及類似的情況,我認為一些極限程序員自動刪除所有未測試代碼的做法是不切實際的,并且可能具有一定的諷刺性。不能總是獲得絕對完美的測試覆蓋率并不意味著就不會有更好的覆蓋率。
然而,比執行不到的語句和方法更常見的是殘留代碼,它不再有任何作用,并且從代碼基中去掉這些代碼也不會產生任何影響。有時可以通過使用反射來訪問私有成員這樣的怪招來測試未測試的代碼。還可以為未測試的、包保護(package-protected)的代碼來編寫測試代碼,將測試類放到將要測試的類所在那個包中。但最好不要這樣做。所有不能通過發布的(公共的和受保護的)接口訪問的代碼都應刪除。執行不到的代碼不應當成為代碼基的一部分。代碼基越小,它就越容易被理解和維護。
![]() |
|
![]() ![]() |
![]()
|
在了解了測量代碼覆蓋率的好處后,讓我們再來討論一下如何用 Cobertura 測量代碼覆蓋率的具體細節。Cobertura 被設計成為在 Ant 中運行?,F在還沒有這方面的 IDE 插件可用,不過一兩年內也許就會有了。
首先需要在 build.xml 文件中添加一個任務定義。以下這個頂級 taskdef
元素將 cobertura.jar 文件限定在當前工作目錄中:
|
然后,需要一個 cobertura-instrument
任務,該任務將在已經編譯好的類文件中添加日志代碼。todir
屬性指定將測量類放到什么地方。fileset
子元素指定測量哪些 .class 文件:
|
用通常運行測試包的同一種類型的 Ant 任務運行測試。惟一的區別在于:被測量的類必須在原始類出現在類路徑中之前出現在類路徑中,而且需要將 Cobertura JAR 文件添加到類路徑中:
|
Jaxen 項目使用 JUnit 作為其測試框架,但是 Cobertura 是不受框架影響的。它在 TestNG、Artima SuiteRunner、HTTPUni 或者在您自己在地下室開發的系統中一樣工作得很好。
最后,cobertura-report
任務生成本文開始部分看到的那個 HTML 文件:
|
srcdir
屬性指定原始的 .java 源代碼在什么地方。destdir
屬性指定 Cobertura 放置輸出 HTML 的那個目錄的名稱。
在自己的 Ant 編譯文件中加入了類似的任務后,就可以通過鍵入以下命令來生成一個覆蓋報告:
|
當然,如果您愿意的話,還可以改變目標任務的名稱,或者將這三項任務合并為一個目標任務。
![]() ![]() |
![]()
|
Cobertura 是敏捷程序員工具箱中新增的一個重要工具。通過生成代碼覆蓋率的具體數值,Cobertura 將單元測試從一種藝術轉變為一門科學。它可以尋找測試覆蓋中的空隙,直接找到 bug。測量代碼覆蓋率使您可以獲得尋找并修復 bug 所需的信息,從而開發出對每個人來說都更健壯的軟件。
![]() ![]() |
![]()
|
- 您可以參閱本文在 developerWorks 全球站點上的 英文原文。
- 從 SourceForge 下載 Cobertura。
- 獲得 JUnit受影響的測試,它是用于 Java 平臺的實際標準單元測試框架。
- 閱讀 Dave Thomas 和 Andy Hunt 合著的 Pragmatic Unit Testing in Java With JUnit(Pragmatic Bookshelf,2003)。
- Cenqua 的 Clover 是一個更精致的、收費的測試覆蓋率工具,它所做的工作實質上與 Cobertura 相同,只是它做得更細致一些。
- IBM Rational 提供了全面的測試工具包來幫助您檢測代碼?!?a >Get started with automated testing: Road map to success”為您提供了即刻開始測試自己的代碼所需的基礎知識。
- Mike Kelly 提供了“using TestManager to report test coverage”的細節(developerWorks,2003 年 12 月)。
-
PureCoverage
是 IBM Rational PurifyPlus 測試工具包的一部分,它是一個出色的代碼覆蓋率工具。Rational Application Developor 包括 PureCoverage 功能,從“使用 IBM Rational Application Developer for WebSphere Software 進行組件測試”中可以了解關于它的更多知識(developerWorks,2005 年 3 月)。
- Cobertura 是 jcoverage 的分支。
- Carnegie Mellon 的 Software Engineering Institute 發表了對 McCabe's Cyclomatic Complexity 的詳細說明。
- David Carew 和 Sandeep Desai 合著的 “Keeping critters out of your code: How to use WebSphere and JUnit to prevent programming bugs”(developerWorks,2003 年 6 月) 分析了如何用 XP 方法進行測試。
- Dennis M. Sosnoski 推出 了 Classworking 工具箱 系列,探討開源的 Hansel 和 Gretel 代碼覆蓋率工具。
- 參閱“Test your tests with Jester”,了解關于開放源代碼 JUnit 測試測試器的知識。
- Malcolm Davis 撰寫的“利用 Ant 和 JUnit 進行增量開發”(developerWorks,2000 年 11 月)介紹了如何將 JUnit 集成到自己的項目中。
- Eric Allen 和 Roy Miller 在他們各自的專欄 診斷 Java 代碼 和 Demystifying Extreme Programming 中經常討論到單元測試。
- Erik Hatcher 的 讓編譯和測試過程自動化 (developerWorks,2001 年 8 月)展示了如何將增量測試和連續編譯結合到一個自動過程中。
- 探索 FoCuS,這是 IBM alphaWorks 的一個工具,它實現了功能覆蓋方法,并通過提供未測試區域的詳細覆蓋率信息來改進應用程序測試。
- 作為本文分析對象的 Jaxen 項目 是一個用于 Java 編程的開源 XPath 引擎,適合多種不同對象模型。
- 在 developerWorks 的 Java 技術專區 中可以找到關于 Java 編程的各個方面的文章。
- 請參閱 Developer Bookstore,以獲得一些技術書籍的完整清單,其中包括數百本 Java 相關主題 的書籍。
![]() ![]() |
![]()
|
![]() |
||
|
![]() |
Elliotte Rusty Harold 出生在新奧爾良,現在他還定期回老家喝一碗美味的秋葵湯。不過目前,他和妻子 Beth 定居在紐約臨近布魯克林的 Prospect Heights,同住的還有他的貓咪 Charm(取自夸克)和 Marjorie(取自他岳母的名字)。他是 Polytechnic 大學計算機科學的副教授,講授 Java 技術和面向對象編程。他的 Cafe au Lait 網站是 Internet 上最受歡迎的獨立 Java 站點之一,姊妹站點 Cafe con Leche 已經成為最受歡迎的 XML 站點之一。他的著作包括 Effective XML、Processing XML with Java、Java Network Programming和 The XML 1.1 Bible。目前他正在從事 XML 的 XOM API、Jaxen XPath 引擎和 Jester 測試覆蓋率工具的開發工作。 |
posted @ 2006-07-19 12:52 eddy liao 閱讀(364) | 評論 (0) | 編輯 收藏
http://www-128.ibm.com/developerworks/cn/java/j-cq01316/#N10149追求代碼質量: 不要被覆蓋報告所迷惑您是否曾被測試覆蓋度量引入歧途? ![]() |
![]() |
![]() |
|
級別: 初級
Andrew Glover
, CTO, Vanward Technologies
2006 年 2 月 06 日
測試覆蓋工具對單元測試具有重要的意義,但是經常被誤用。這個月,Andrew Glover 會在他的新系列 —— 追求代碼質量 中向您介紹值得參考的專家意見。第一部分深入地介紹覆蓋報告中數字的真實含義。然后他會提出您可以盡早并經常地利用覆蓋來確保代碼質量的三個方法。
您還記得以前大多數開發人員是如何追求代碼質量的嗎。在那時,有技巧地放置 main()
方法被視為靈活且適當的測試方法。經歷了漫長的道路以后,現在自動測試已經成為高質量代碼開發的基本保證,對此我很感謝。但是這還不是我所要感謝的全部。Java? 開發人員現在擁有很多通過代碼度量、靜態分析等方法來度量代碼質量的工具。我們甚至已經設法將重構分類成一系列便利的模式!
![]() |
|
所有的這些新的工具使得確保代碼質量比以前簡單得多,不過您還需要知道如何使用它們。在這個系列中,我將重點闡述有關保證代碼質量的一些有時看上去有點神秘的東西。除了帶您一起熟悉有關代碼質量保證的眾多工具和技術之外,我還將為您說明:
- 定義并有效度量最影響質量的代碼方面。
- 設定質量保證目標并照此規劃您的開發過程。
- 確定哪個代碼質量工具和技術可以滿足您的需要。
- 實現最佳實踐(清除不好的),使確保代碼質量及早并經常地 成為開發實踐中輕松且有效的方面。
在這個月,我將首先看看 Java 開發人員中最流行也是最容易的質量保證工具包:測試覆蓋度量。
這是一個晚上鏖戰后的早晨,大家都站在飲水機邊上。開發人員和管理人員們了解到一些經過良好測試的類可以達到超過 90% 的覆蓋率,正在高興地互換著 NFL 風格的點心。團隊的集體信心空前高漲。從遠處可以聽到 “放任地重構吧” 的聲音,似乎缺陷已成為遙遠的記憶,響應性也已微不足道。但是一個很小的反對聲在說:
女士們,先生們,不要被覆蓋報告所愚弄。
現在,不要誤解我的意思:并不是說使用測試覆蓋工具是愚蠢的。對單元測試范例,它是很重要的。不過更重要的是您如何理解所得到的信息。許多開發團隊會在這兒犯第一個錯。
高覆蓋率只是表示執行了很多的代碼,并不意味著這些代碼被很好地 執行。如果您關注的是代碼的質量,就必須精確地理解測試覆蓋工具能做什么,不能做什么。然后您才能知道如何使用這些工具去獲取有用的信息。而不是像許多開發人員那樣,只是滿足于高覆蓋率。
測試覆蓋度量
測試覆蓋工具通??梢院苋菀椎靥砑拥酱_定的單元測試過程中,而且結果可靠。下載一個可用的工具,對您的 Ant 和 Maven 構建腳本作一些小的改動,您和您的同事就有了在飲水機邊上談論的一種新報告:測試覆蓋報告。當 foo
和 bar
這樣的程序包令人驚奇地顯示高 覆蓋率時,您可以得到不小的安慰。如果您相信至少您的部分代碼可以保證是 “沒有 BUG” 的,您會覺得很安心。但是這樣做是一個錯誤。
存在不同類型的覆蓋度量,但是絕大多數的工具會關注行覆蓋,也叫做語句覆蓋。此外,有些工具會報告分支覆蓋。通過用一個測試工具執行代碼庫并捕獲整個測試過程中與被 “觸及” 的代碼對應的數據,就可以獲得測試覆蓋度量。然后這些數據被合成為覆蓋報告。在 Java 世界中,這個測試工具通常是 JUnit 以及名為 Cobertura、Emma 或 Clover 等的覆蓋工具。
行覆蓋只是指出代碼的哪些行被執行。如果一個方法有 10 行代碼,其中的 8 行在測試中被執行,那么這個方法的行覆蓋率是 80%。這個過程在總體層次上也工作得很好:如果一個類有 100 行代碼,其中的 45 行被觸及,那么這個類的行覆蓋率就是 45%。同樣,如果一個代碼庫包含 10000 個非注釋性的代碼行,在特定的測試運行中有 3500 行被執行,那么這段代碼的行覆蓋率就是 35%。
報告分支覆蓋 的工具試圖度量決策點(比如包含邏輯 AND
或 OR
的條件塊)的覆蓋率。與行覆蓋一樣,如果在特定方法中有兩個分支,并且兩個分支在測試中都被覆蓋,那么您可以說這個方法有 100% 的分支覆蓋率。
問題是,這些度量有什么用?很明顯,很容易獲得所有這些信息,不過您需要知道如何使用它們。一些例子可以闡明我的觀點。
![]() ![]() |
![]()
|
我在清單 1 中創建了一個簡單的類以具體表述類層次的概念。一個給定的類可以有一連串的父類,例如 Vector
,它的父類是 AbstractList
,AbstractList
的父類又是 AbstractCollection
,AbstractCollection
的父類又是 Object
:
清單 1. 表現類層次的類
|
正如您看到的,清單 1 中的 Hierarchy
類具有一個 baseClass
實例以及它的父類的集合。清單 2 中的 HierarchyBuilder
通過兩個復制 buildHierarchy
的重載的 static
方法創建了 Hierarchy
類。
清單 2. 類層次生成器
|
![]() ![]() |
![]()
|
有關測試覆蓋的文章怎么能缺少測試案例呢?在清單 3 中,我定義了一個簡單的有三個測試案例的 JUnit 測試類,它將試圖執行 Hierarchy
類和 HierarchyBuilder
類:
清單 3. 測試 HierarchyBuilder!
|
因為我是一個狂熱的測試人員,我自然希望運行一些覆蓋測試。對于 Java 開發人員可用的代碼覆蓋工具中,我比較喜歡用 Cobertura,因為它的報告很友好。而且,Corbertura 是開放源碼項目,它派生出了 JCoverage 項目的前身。
![]() ![]() |
![]()
|
運行 Cobertura 這樣的工具和運行您的 JUnit 測試一樣簡單,只是有一個用專門邏輯在測試時檢查代碼以報告覆蓋率的中間步驟(這都是通過工具的 Ant 任務或 Maven 的目標完成的)。
正如您在圖 1 中看到的,HierarchyBuilder
的覆蓋報告說明部分代碼沒有 被執行。事實上,Cobertura 認為 HierarchyBuilder
的行覆蓋率為 59%,分支覆蓋率為 75%。
圖 1. Cobertura 的報告

這樣看來,我的第一次覆蓋測試是失敗的。首先,帶有 String
參數的 buildHierarchy()
方法根本沒有被測試。其次,另一個 buildHierarchy()
方法中的兩個條件都沒有被執行。有趣的是,所要關注的正是第二個沒有被執行的 if
塊。
因為我所需要做的只是增加一些測試案例,所以我并不擔心這一點。一旦我到達了所關注的區域,我就可以很好地完成工作。注意我這兒的邏輯:我使用測試報告來了解什么沒有 被測試。現在我已經可以選擇使用這些數據來增強測試或者繼續工作。在本例中,我準備增強我的測試,因為我還有一些重要的區域未覆蓋。
清單 4 是一個更新過的 JUnit 測試案例,增加了一些附加測試案例,以試圖完全執行 HierarchyBuilder
:
清單 4. 更新過的 JUnit 測試案例
|
當我使用新的測試案例再次執行測試覆蓋過程時,我得到了如圖 2 所示的更加完整的報告?,F在,我覆蓋了未測試的 buildHierarchy()
方法,也處理了另一個 buildHierarchy()
方法中的兩個 if
塊。然而,因為 HierarchyBuilder
的構造器是 private
類型的,所以我不能通過我的測試類測試它(我也不關心)。因此,我的行覆蓋率仍然只有 88%。
圖 2. 誰說沒有第二次機會

正如您看到的,使用一個代碼覆蓋工具可以 揭露重要的沒有相應測試案例的代碼。重要的事情是,在閱讀報告(特別 是覆蓋率高的)時需要小心,它們也許隱含危險的信息。讓我們看看兩個例子,看看在高覆蓋率后面隱藏著什么。
![]() ![]() |
![]()
|
正如您已經知道的,代碼中的許多變量可能有多種狀態;此外,條件的存在使得執行有多條路徑。在留意這些問題之后,我將在清單 5 中定義一個極其簡單只有一個方法的類:
清單 5.您能看出下面的缺陷嗎?
|
您是否發現了清單 5 中有一個隱藏的缺陷呢?如果沒有,不要擔心,我會在清單 6 中寫一個測試案例來執行 pathExample()
方法并確保它正確地工作:
清單 6. JUnit 來救援!
|
我的測試案例正確運行,我的神奇的代碼覆蓋報告(如下面圖 3 所示)使我看上去像個超級明星,測試覆蓋率達到了 100%!
圖 3. 覆蓋率明星

我想現在應該到飲水機邊上去說了,但是等等,我不是懷疑代碼中有什么缺陷呢?認真檢查清單 5 會發現,如果 condition
為 false
,那么第 13 行確實會拋出 NullPointerException
。Yeesh,這兒發生了什么?
這表明行覆蓋的確不能很好地指示測試的有效性。
![]() ![]() |
![]()
|
在清單 7 中,我定義了另一個包含 indirect 的簡單例子,它仍然有不能容忍的缺陷。請注意 branchIt()
方法中 if
條件的后半部分。(HiddenObject
類將在清單 8 中定義。)
清單 7. 這個代碼足夠簡單
|
呀!清單 8 中的 HiddenObject
是有害的。與清單 7 中一樣,調用 doWork()
方法會導致 RuntimeException
:
清單 8. 上半部分!
|
但是我的確可以通過一個良好的測試捕獲這個異常!在清單 9 中,我編寫了另一個好的測試,以圖挽回我的超級明星光環:
清單 9. 使用 JUnit 規避風險
|
您對這個測試案例有什么想法?您也許會寫出更多的測試案例,但是請設想一下清單 7 中不確定的條件有不止一個的縮短操作會如何。設想如果前半部分中的邏輯比簡單的 int
比較更復雜,那么您 需要寫多少測試案例才能滿意?
現在,對清單 7、8、9 的測試覆蓋率的分析結果不再會使您感到驚訝。在圖 4 的報告中顯示我達到了 75% 的行覆蓋率和 100% 的分支覆蓋率。最重要的是,我執行了第 10 行!
圖 4.愚弄的報酬

從第一印象看,這讓我驕傲。但是這個報告有什么誤導嗎?只是粗略地看一看報告中的數字,會導致您相信代碼是經過良好測試的?;谶@一點,您也許會認為出現缺陷的風險很低。這個報告并不能幫助您確定 or
縮短操作的后半部分是一個定時炸彈!
![]() ![]() |
![]()
|
我不止一次地說:您可以(而且應該)使用測試覆蓋工具作為您的測試過程的一部分。但是不要被覆蓋報告所愚弄。關于覆蓋報告您需要了解的主要事情是,覆蓋報告最好用來檢查哪些代碼沒有經過 充分的測試。當您檢查覆蓋報告時,找出較低的值,并了解為什么特定的代碼沒有經過充分的測試。知道這些以后,開發人員、管理人員以及 QA 專業人員就可以在真正需要的地方使用測試覆蓋工具。通常有下列三種情況:
- 估計修改已有代碼所需的時間
- 評估代碼質量
- 評定功能測試
現在我可以斷定對測試覆蓋報告的一些使用方法會將您引入歧途,下面這些最佳實踐可以使得測試覆蓋報告可以真正為您所用。
對一個開發團隊而言,針對代碼編寫測試案例自然可以增加集體的信心。與沒有相應測試案例的代碼相比,經過測試的代碼更容易重構、維護和增強。測試案例因為暗示了代碼在測試工作中是如何 工作的,所以還可以充當內行的文檔。此外,如果被測試的代碼發生改變,測試案例通常也會作相應的改變,這與諸如注釋和 Javadoc 這樣的靜態代碼文檔不同。
在另一方面,沒有經過相應測試的代碼更難于理解和安全地 修改。因此,知道代碼有沒有被測試,并看看實際的測試覆蓋數值,可以讓開發人員和管理人員更準確地預知修改已有代碼所需的時間。
再次回到飲水機邊上,可以更好地闡明我的觀點。
市場部的 Linda:“我們想讓系統在用戶完成一筆交易時做 x 工作。這需要多長時間。我們的用戶需要盡快實現這一功能?!?
管理人員 Jeff:“讓我看看,這個代碼是 Joe 在幾個月前編寫的,需要對業務層和 UI 做一些變動。Mary 也許可以在兩天內完成這項工作?!?
Linda:“Joe?他是誰?”
Jeff:“哦,Joe,因為他不知道自己在干什么,所以被我解雇了?!?
情況似乎有點不妙,不是嗎?盡管如此,Jeff 還是將任務分配給了 Mary,Mary 也認為能夠在兩天內完成工作 —— 確切地說,在看到代碼之前她是這么認為的。
Mary:“Joe 寫這些代碼時是不是睡著了?這是我所見過的最差的代碼。我甚至不能確認這是 Java 代碼。除非推倒重來,要不我根本沒法修改。”
情況對 “飲水機” 團隊不妙,不是嗎?但是我們假設,如果在這個不幸的事件的當初,Jeff 和 Mary 就擁有一份測試報告,那么情況會如何呢?當 Linda 要求實現新功能時,Jeff 做的第一件事就是檢查以前生成的覆蓋報告。注意到需要改動的軟件包幾乎沒有被覆蓋,然后他就會與 Mary 商量。
Jeff:“Joe 編寫的這個代碼很差,絕大多數沒經過測試。您認為要支持 Linda 所說的功能需要多長時間?”
Mary:“這個代碼很混亂。我甚至都不想看到它。為什么不讓 Mark 來做呢?”
Jeff:“因為 Mark 不編寫測試,剛被我解雇了。我需要您測試這個代碼并作一些改動。告訴我您需要多長時間?!?
Mary:“我至少需要兩天編寫測試,然后我會重構這個代碼,增加新的功能。我想總共需要四天吧。”
正如他們所說的,知識的力量是強大的。開發人員可以在試圖修改代碼之前 使用覆蓋報告來檢查代碼質量。同樣,管理人員可以使用覆蓋數據更好地估計開發人員實際所需的時間。
開發人員的測試可以降低代碼中存在缺陷的風險,因此現在很多開發團隊在新開發和更改代碼的同時需要編寫單元測試。然而正如前面所提到的 Mark 一樣,并不總是在編碼的同時進行單元測試,因而會導致低質量代碼的出現。
監控覆蓋報告可以幫助開發團隊迅速找出不斷增長的沒有 相應測試的代碼。例如,在一周開始時運行覆蓋報告,顯示項目中一個關鍵的軟件包的覆蓋率是 70%。如果幾天后,覆蓋率下降到了 60%,那么您可以推斷:
- 軟件包的代碼行增加了,但是沒有為新代碼編寫相應的測試(或者是新增加的測試不能有效地覆蓋新代碼)。
- 刪除了測試案例。
- 上述兩種情況都發生了。
能夠監控事情的發展,無疑是件好事。定期地查閱報告使得設定目標(例如獲得覆蓋率、維護代碼行的測試案例的比例等)并監控事情的發展變得更為容易。如果您發現測試沒有如期編寫,您可以提前采取一些行動,例如對開發人員進行培訓、指導或幫助。與其讓用戶 “在使用中” 發現程序缺陷(這些缺陷本應該在幾個月前通過簡單的測試暴露出來),或者等到管理人員發現沒有編寫單元測試時再感到驚訝(和憤怒),還不如采取一些預防性的措施。
使用覆蓋報告來確保正確的測試是一項偉大的實踐。關鍵是要訓練有素地完成這項工作。例如,使每晚生成并查閱覆蓋報告成為連續累計 過程的一部分。
假設覆蓋報告在指出沒有經過 足夠測試的代碼部分方面非常有效,那么質量保證人員可以使用這些數據來評定與功能測試有關的關注區域。讓我們回到 “飲水機” 團隊來看看 QA 的負責人 Drew 是如何評價 Joe 的代碼的:
Drew 對 Jeff 說:“我們為下一個版本編寫了測試案例,我們注意到很多代碼沒有被覆蓋。那好像是與股票交易有關的代碼。”
Jeff:“哦,我們在這個領域有好些問題。如果我是一個賭徒的話,我會對這個功能區域給予特別的關注。Mary 正在對這個應用程序做一些其他的修改 —— 她在編寫單元測試方面做得很好,但是這個代碼也太差了點?!?
Drew:“是的,我正在確定工作的資源和級別,看上去我沒必要那么擔心了,我估計我們的團隊會對股票交易模塊引起足夠的關注?!?
知識再次顯示了其強大的力量。與其他軟件生命周期中的風險承擔者(例如 QA)配合,您可以利用覆蓋報告所提供的信息來降低風險。在上面的場景中,也許 Jeff 可以為 Drew 的團隊提供一個早期的不包含 Mary 的所有修改的版本。不過無論如何,Drew 的團隊都應該關注應用程序的股票交易方面,與其他具有相應單元測試的代碼相比,這個地方似乎存在更大的缺陷風險。
![]() ![]() |
![]()
|
對單元測試范例而言,測試覆蓋度量工具是一個有點奇怪的組成部分。對于一個已存在的有益的過程,覆蓋度量可以增加其深度和精度。然而,您應該仔細地閱讀代碼覆蓋報告。單獨的高覆蓋率并不能確保代碼的質量。對于減少缺陷,代碼的高覆蓋并不是必要條件,盡管高覆蓋的代碼的確更少 有缺陷。
測試覆蓋度量的竅門是使用覆蓋報告找出未經 測試的代碼,分別在微觀和宏觀兩個級別。通過從頂層開始分析您的代碼庫,以及分析單個類的覆蓋,可以促進深入的覆蓋測試。一旦您能夠綜合這些原則,您和您的組織就可以在真正需要的地方使用覆蓋度量工具,例如估計一個項目所需的時間,持續監控代碼質量以及促進與 QA 的協作。
![]() ![]() |
![]()
|
-
參與論壇討論
。
- 您可以參閱本文在 developerWorks 全球站點上的 英文原文。
- “用 Cobertura 測量測試覆蓋率”(Elliotte Rusty Harold,developerWorks,2005 年 5 月):找出存在 bug 的未經測試的代碼。
- “Classworking 工具箱: 用 Hansel 和 Gretel 覆蓋代碼”(Dennis Sosnoski,developerWorks,2005 年 2 月):如何完成您的單元測試?使用代碼覆蓋工具找出答案!
- “用 Jester 對測試進行測試”(Elliotte Rusty Harold,developerWorks,2005 年 5 月):Jester 擅長發現測試套件的問題,對代碼庫的結構方面有獨特的洞察力。
- “The business value of software quality”(Geoffrey Bessin,The Rational Edge,2004 年 6 月):描述 IBM Rational 有關提高代碼質量的主張并列舉出用來保證質量的主要工具。
- Java 技術專區 :數百篇有關 Java 編程各方面的文章。
![]() ![]() |
![]()
|
![]() |
||
|
![]() |
Andrew Glover 是 Vanward Technologies 公司的 CTO,該公司位于華盛頓特區大都會地區,公司的專業領域是自動測試框架的構造,自動測試框架可以降低軟件 bug 數量,減少集成和測試的時間,提高整體的代碼穩定性。他還是 Java Testing Patterns(Wiley,2004 年 9 月)的合著者 |
posted @ 2006-07-19 12:48 eddy liao 閱讀(288) | 評論 (0) | 編輯 收藏