使用 EclEmma 進行覆蓋測試
覆蓋測試是衡量測試質量的一個重要指標。在對一個軟件產品進行了單元測試、組裝測試、集成測試以及接受測試等繁多的測試之后,我們能不能就此對軟件的質量產生一定的信心呢?這就需要我們對測試的質量進行考察。如果測試僅覆蓋了代碼的一小部分,那么不管我們寫了多少測試用例,我們也不能相信軟件質量是有保證的。相反,如果測試覆蓋到了軟件的絕大部分代碼,我們就能對軟件的質量有一個合理的信心。本文將介紹一個優秀的開源軟件測試工具 EclEmma,它能夠對由 Java 語言編寫的程序進行覆蓋測試,從而對程序運行的結果生成詳盡的覆蓋測試報告。
2007 年 5 月 10 日
介紹
現在 IT 開發人員比以往任何時候都更加關注測試的重要性,沒有經過良好測試的代碼更容易出問題。在極限編程中,測試驅動開發已經被證明是一種有效提高軟件質量的方法。在測試驅動的開發方式中,軟件工程師在編寫功能代碼之前首先編寫測試代碼,這樣能從最開始保證程序代碼的正確性,并且能夠在程序的每次演進時進行自動的回歸測試。測試對于軟件產品的成敗起著至關重要的作用,在極限編程領域,甚至有人提議任何未經測試的代碼都應該自動從發布的產品中刪除。作者并不確信這個觀點是正確的,但是測試本身的質量確實是一個需要高度關注的問題。測試的覆蓋率是測試質量的一個重要指標,我們需要工具來幫助我們進行對軟件測試覆蓋的考察。
EclEmma 就是這樣一個能幫助開發人員考察測試覆蓋率的優秀的 Eclipse 開源插件。EclEmma 在覆蓋測試領域是如此的優秀,以致于它在過去不久的 2006 年成為了 Eclipse Community Awards Winners 決賽選手。雖然最后 Eclipse Checkstyle Plugin 取得了 Best Open Source Eclipse-based Developer tool 的稱號,但我們也可以由此看到 EclEmma 對開發人員的幫助是巨大的(Eclipse Community Award 的具體信息可以參閱 參考資源)。
提到 EclEmma 首先就要說到著名的 Java 覆蓋測試工具 Emma。Emma 是一個在 SourceForge 上進行的開源項目(參閱 參考資源)。從某種程度上說,EclEmma 可以看作是 Emma 的一個圖形界面。在本文的參考文獻中,可以看到專門講述使用 Emma 的技術文章。
Emma 的作者開發 Emma 之初,程序員已經有了各種各樣優秀的開源 Java 開發工具。舉例來說,我們有優秀的集成開發環境 Eclipse,有開源的 JDK,有單元測試工具 JUnit,有 Ant 這樣的項目管理工具,我們還可以用 CVS 或 SubVersion 來進行源代碼版本的維護。當時看來,也許唯一缺少的就是一個開源的覆蓋測試工具了。Emma 就是為了填補這項空白而生的?,F在的情況已經和 Emma 誕生的時候不一樣的。時至今日,我們已經有了不少的覆蓋測試工具。例如 Coverlipse 是一個基于 Eclipse 的覆蓋測試插件。其他還有 Cobertura,Quilt 和 JCoverage 等。但是 Emma 具有一些非常優秀的特性使得它更適合被廣泛的使用。和 Coverlipse 等工具比起來,Emma 是開源的,同時它對應用程序執行速度的影響非常小。
EclEmma 的出現彌補了 Emma 用戶一個大的遺憾 ---- 缺乏圖形界面以及對集成開發環境的支持。將 Eclipse 和 Emma 這兩個在各自領域最為優秀的工具結合起來,這就是 EclEmma 為我們提供的。接下來,我們就要在后續章節中和讀者朋友一起看看 EclEmma 為開發人員提供了什么。
安裝 EclEmma 插件
安裝 EclEmma 插件的過程和大部分 Eclipse 插件相同,我們既可以通過 Eclipse 標準的 Update 機制來遠程安裝 EclEmma 插件(圖 1),也可以從站點(參閱 參考資源)下載 zip 文件并解壓到 eclipse 所在的目錄中。
圖 1 添加 EclEmma 更新站點

不管采用何種方式來安裝 EclEmma,安裝完成并重新啟動 Eclipse 之后,工具欄上應該出現一個新的按鈕:
圖 2 新增的覆蓋測試按鈕

使用 EclEmma 測試 Java 程序
為了實驗 EclEmma 的特性,我們首先在 Eclipse 的 Workspace 中建立一個名稱為 test.emma 的新 Java 項目。接下來,我們在其中建立一個 HelloWorld
類,其代碼如下所示:
清單 1 用于測試 EclEmma 的代碼
package test.emma; public class HelloWorld { /** * @param args */ public static void main(String[] args) { int rand = (int) (Math.random()*100); if(rand%2==0){ System.out.println( "Hello, world! 0"); } else System.out.println("Hello, world! 1"); int result = rand%2==0? rand+rand:rand*rand; System.out.println(result); } }
接下來,我們通過 EclEmma 運行 HelloWorld.main()
函數。
圖 3 對 Java 應用程序進行覆蓋測試

執行完畢之后,我們正在編輯 HelloWorld.java 的窗口將會變成如下所示:
圖 4 進行覆蓋測試的結果

在 Java 編輯器中,EclEmma 用不同的色彩標示了源代碼的測試情況。其中,綠色的行表示該行代碼被完整的執行,紅色部分表示該行代碼根本沒有被執行,而黃色的行表明該行代碼部分被執行。黃色的行通常出現在單行代碼包含分支的情況,例如 圖 4 中的 16 行就顯示為黃色。由于程序中有一個隨機確定的分支,因此讀者的窗口可能與這里稍有不同(11 行或者 14 行中有且只有一個紅色的行)。
除了在源代碼編輯窗口直接進行著色之外,EclEmma 還提供了一個單獨的視圖來統計程序的覆蓋測試率。
圖 5 察看程序的覆蓋測試率

EclEmma 提供的 Coverage 視圖能夠分層的顯示代碼的覆蓋測試率,圖 5 中的信息表明我們對 HelloWorld 的一次運行覆蓋了大約 68.6% 的代碼。
想在一次運行中覆蓋所有的代碼通常比較困難,如果能把多次測試的覆蓋數據綜合起來進行察看,那么我們就能更方便的掌握多次測試的測試效果。EclEmma 提供了這樣的功能?,F在,讓我們重復數次對 HelloWorld 的覆蓋測試。我們注意到 Coverage 視圖總是顯示最新完成的一次覆蓋測試。事實上,EclEmma 為我們保存了所有的測試結果。接下來,我們將通過 Coverage 視圖的工具按鈕來結合多次覆蓋測試的結果。
圖 6 用于結合多次覆蓋測試結果的工具欄按鈕

當我們多次運行 Coverage 之后,我們可以單擊 圖 6 所示工具欄按鈕。之后,一個對話框將被彈出以供用戶選擇需要合并的覆蓋測試。
圖 7 選擇需要合并的覆蓋測試結果

在合并完成之后,我們可以觀察到 Java 編輯器和 Coverage 視圖中都顯示了合并之后的結果:
圖 8 察看合并后的覆蓋測試結果

圖 8 中,我們可以看到,通過多次運行覆蓋測試,最終我們的代碼達到了 91.4% 的測試覆蓋率。有趣的是,圖中第三行代碼被標記為紅色,而此行代碼實際上是不可執行的。奧妙在于,我們沒有生成任何 HelloWorld 類的實例,因此缺省構造函數沒有被調用,而 EclEmma 將這個特殊代碼的覆蓋狀態標記在類聲明的第一行。
EclEmma 的高級特性
如果 EclEmma 只能測試 Java Application 的測試覆蓋率,那么它相對命令行版本的 Emma 來說,提供的增強就不多了。相反,EclEmma 提供了很多與 Eclipse 緊密結合的功能。它不僅能測試 Java Application,還能計算 JUnit 單元測試,對 Eclipse 插件測試的覆蓋率。從 圖 9 中我們可以看到 EclEmma 目前支持四種類型的程序。
圖 9 EclEmma 的配置頁面

為了了解 EclEmma 是如何獲得覆蓋測試數據的,我們需要先對 Emma 有初步的了解。通常代碼覆蓋測試工具都需要對被執行的代碼進行修改。而 Emma 提供了兩種方式來完成這件事。
- 預插入模式:對程序進行測量之前,需要采用 Emma 提供的工具對 class 文件或者 jar 文件進行修改。修改完成之后的代碼可以立刻被執行。覆蓋測試的結果將會被存放到指定的文件中。
- 即時插入模式:即時插入模式不需要事先對代碼進行修改。相反,對代碼的修改是通過一個 Emma 定制的 Class loader(類載入器)進行的。這種方式的優點很明顯,我們不需要對 class 或者 jar 文件進行任何修改。缺點是我們為了獲得測試的結果,需要用 Emma 提供的命令 emmarun 來執行 Java 應用程序。
使用即時插入模式的優點很明顯:class 文件和 jar 文件不會被修改。而預插入模式的應用范圍更為廣泛,對于某些需要嵌入到框架中運行的代碼來說(例如 EJB),我們只能使用預插入模式。EclEmma 僅僅使用了 Emma 的預插入模式來工作,不過 EclEmma 缺省會在臨時目錄中創建 class 文件和 jar 文件的副本來進行修改,因此在 workspace 中 class 和 jar 文件仍然保持原樣。雖然聽上去很好,但是由于需要修改 classpath 來使用修改過的 class 和 jar 文件,對于不能修改 classpath 的應用(例如 Eclipse RCP 和 JUnit Plugin Test)來說,我們還是只能選擇修改 workspace 中的 class 文件和 jar 文件。對于 Java Application 和 JUnit 類型的覆蓋測試,我們可以在配置對話框中選中“In-place instrumentation”項來指定直接修改 Workspace 中的 .class 文件和 .jar 文件。