您好,登錄后才能下訂單哦!
這篇文章給大家分享的是有關怎么使用JaCoCo分析java單元測試覆蓋率的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。
前言
隨著敏捷開發的流行,編寫單元測試已經成為業界共識。但如何來衡量單元測試的質量呢?有些管理者片面追求單元測試的數量,導致底下的開發人員投機取巧,編寫出大量的重復測試,數量上去了,質量卻依然原地踏步。相比單純追求單元測試的數量,分析單元測試的代碼覆蓋率是一種更為可行的方式。
JaCoCo(Java Code Coverage)就是一種分析單元測試覆蓋率的工具,使用它運行單元測試后,可以給出代碼中哪些部分被單元測試測到,哪些部分沒有沒測到,并且給出整個項目的單元測試覆蓋情況百分比,看上去一目了然。
EclEmma 是基于 JaCoCo 的一個 Eclipse 插件,開發人員可以方便的和其交互。因此,本文先從 EclEmma 入手,給讀者一個直觀的體驗。
使用 EclEmma 在 Eclipse 中查看單元測試覆蓋率
EclEmma 是基于 JaCoCo 的 Eclipse 插件,使用它,開發人員可以直觀地看到單元測試的覆蓋情況。
安裝 EclEmma
打開 Eclipse 的軟件市場,在其中搜索 EclEmma,找到后完成安裝,如下圖所示:
圖 1. 安裝 EclEmma
安裝完成后,Eclipse 的工具條里會多出下面這樣一個圖標:
圖 2. Coverage 圖標
分析單元測試覆蓋率
成功安裝 EclEmma 后,就可以試著用它來分析項目的單元測試覆蓋率了。為了方便演示,我們使用 Eclipse 創建了一個標準 Java 工程。其中包含一個數學工具類,用來計算三個數中的最大值,代碼如下:
清單 1. 數學工具類
package com.dw.math; public class MathUtil { public static int max(int a, int b, int c){ if(a > b){ if(a > c){ return a; }else{ return c; } }else{ if(b > c){ return b; }else{ return c; } } } }
可以看到,這里的算法稍微有點復雜,使用到了多個條件判斷分支,因此,特別適合為其編寫單元測試。第一個版本的單元測試如下:
清單 2. 第一個版本的單元測試
package com.dw.math; import static org.junit.Assert.*; import org.junit.Test; public class MathUtilTest { @Test public void test_max_1_2_3() { assertEquals(3, MathUtil.max(1, 2, 3)); } }
試著運行一下單元測試覆蓋率分析工具:40.0%!似乎不太理想。展開分析報告,雙擊后在編輯器里可以看到覆蓋情況被不同的顏色標識出來,其中綠顏色表示代碼被單元測試覆蓋到,黃色表示部分覆蓋,紅色則表示完全沒有覆蓋到,如下圖所示:
圖 3. 單元測試覆蓋率報告
讓我們嘗試多加一些單元測試,來改善這種情況,請看下面第二個版本的單元測試:
清單 3. 第二個版本的單元測試
package com.dw.math; import static org.junit.Assert.*; import org.junit.Test; public class MathUtilTest { @Test public void test_max_1_2_3() { assertEquals(3, MathUtil.max(1, 2, 3)); } @Test public void test_max_10_20_30() { assertEquals(30, MathUtil.max(10, 20, 30)); } @Test public void test_max_100_200_300() { assertEquals(300, MathUtil.max(100, 200, 300)); } }
測試覆蓋率還是 40.0%!雖然我們額外再加了兩個測試,但覆蓋率沒有半點提升,這些單元測試其實是重復的,它們在重復測試同一段代碼。如果單純追求單元測試的數量,那么這無疑會給管理者造成錯覺,他們覺得單元測試的數量增加了,軟件的質量更有保證了;而對于那些喜歡偷懶的程序員,也蒙混過關,但卻給軟件質量埋下了隱患。讓我們刪掉這些重復的單元測試,重新思考一下怎么測試這個方法。
首先我們要測試正常情況,這其中又包含 3 種情況:第一個參數最大,第二個參數最大,以及最后一個參數最大。然后我們還需測試幾種特殊情況,比如三個參數相同,三個參數中,其中兩個相同。讓我們照此思路重新編寫單元測試:
清單 4. 第三個版本的單元測試
package com.dw.math; import static org.junit.Assert.*; import org.junit.Test; public class MathUtilTest { @Test public void test_max_1_2_3() { assertEquals(3, MathUtil.max(1, 2, 3)); } @Test public void test_max_1_3_2() { assertEquals(3, MathUtil.max(1, 3, 2)); } @Test public void test_max_3_2_1() { assertEquals(3, MathUtil.max(3, 2, 1)); } @Test public void test_max_0_0_0(){ assertEquals(0, MathUtil.max(0, 0, 0)); } @Test public void test_max_0_1_0(){ assertEquals(1, MathUtil.max(0, 1, 0)); } }
再次運行單元測試分析工具:75.0%!這次比以前有了很大提升,但是結果還不能令人滿意,打開分析報告可以看到,有一個分支還是沒有覆蓋到,如圖所示:
圖 4. 單元測試覆蓋率報告
閱讀代碼可以看出,這種情況是指第一個參數大于第二個參數,卻小于第三個參數,因此我們再增加一個單元測試:
清單 5. 再增加一個單元測試
@Test public void test_max_2_1_3() { assertEquals(3, MathUtil.max(2, 1, 3)); }
再運行一遍單元測試分析工具:100.0%!終于我們的單元測試達到了全覆蓋,這樣我們對自己開發的代碼更有信心了。當然,我們在這里并不是為了單純的追求這個數字,在增加單元測試覆蓋率的誘導下,我們重新理清了測試的步驟,寫出了更有意義、更全面的單元測試。而且根據單元測試分析工具給的反饋,我們還發現了先前沒有想到的情形。因此,單元測試的覆蓋率并不只是一個為了取悅管理者的數據,它實實在在地幫助我們改善了代碼的質量,增加了我們對所編寫代碼的信心。
給管理者的單元測試覆蓋率報告
管理者天生喜歡閱讀報告。他們不會屈尊坐在你的辦公桌前,讓你向他展示 Eclipse 中這一片花花綠綠的東西。而且這份報告對他們至關重要,他們需要用它向上級匯報;年底回顧時,他們也可以興奮地宣稱產品的單元測試覆蓋率增加了多少。作為一名開發人員,我們很大一部分工作量就在于滿足管理者的這種需求。因此,本節我們討論如何將 JaCoCo 集成到 Ant 腳本中,生成漂亮的單元測試覆蓋率報告。
準備工作
在集成 JaCoCo 前,必須先確保你的 Java 工程有一個可執行的 Ant 構建腳本。一個簡單的 Ant 構建腳本一般會執行如下任務:編譯(包括編譯工程代碼和測試代碼)、打包和執行單元測試。下面是本文示例 Java 項目所用的 Ant 構建腳本,讀者可結合自己的項目及文件路徑,在此基礎之上進行修改。
清單 6. build.xml
<project name="math" basedir="." default="junit"> <!--預定義的屬性和 classpath --> <property name="src.dir" value="src" /> <property name="test.dir" value="test" /> <property name="build.dir" value="build" /> <property name="classes.dir" value="${build.dir}/classes" /> <property name="tests.dir" value="${build.dir}/tests" /> <property name="jar.dir" value="${build.dir}/jar" /> <property name="lib.dir" value="lib" /> <path id="classpath"> <fileset dir="${lib.dir}" includes="**/*.jar" /> </path> <!--清除上次構建 --> <target name="clean"> <delete dir="${build.dir}" /> </target> <!--編譯代碼,包括單元測試 --> <target name="compile" depends="clean"> <mkdir dir="${classes.dir}" /> <mkdir dir="${tests.dir}" /> <javac srcdir="${src.dir}" destdir="${classes.dir}" /> <javac srcdir="${test.dir}" destdir="${tests.dir}"> <classpath> <path refid="classpath" /> <path location="${classes.dir}" /> </classpath> </javac> </target> <!--打包 --> <target name="jar" depends="compile"> <mkdir dir="${jar.dir}" /> <jar destfile="${jar.dir}/${ant.project.name}.jar" basedir="${classes.dir}"> </jar> </target> <!--運行單元測試 --> <target name="junit" depends="jar"> <junit printsummary="yes"> <classpath> <path refid="classpath"/> <path location="${classes.dir}" /> <path location="${tests.dir}" /> </classpath> <batchtest fork="yes"> <fileset dir="${test.dir}" includes="**/*Test.java"/> </batchtest> </junit> </target> </project>
集成 JaCoCo
首先需要從 然后就是使用 JaCoCo 官網下載 需要的版本,然后將下載得到的壓縮文件解壓,將其中的 jacocoant.jar 拷貝至 Java 工程下存放第三方 jar 包的目錄,在示例工程里,我有一個和 src 平級的 lib 目錄,jacocoant.jar 就放到了這個目錄底下,讀者可根據自己的項目組織結構做相應調整。然后我們需要在 Ant 構建腳本中定義新的任務:
清單 7. 定義新的構建任務
<taskdef uri="antlib:org.jacoco.ant" resource="org/jacoco/ant/antlib.xml"> <classpath refid="classpath" /> </taskdef>
現在就可以在 Ant 構建腳本中使用 JaCoCo 了。需要注意的是,為了避免命名沖突,需要給 Ant 構建腳本加入新的 XML 命名空間:
清單 8. 加入新的 JaCoCo 命名空間
<project name="math" basedir="." xmlns:jacoco="antlib:org.jacoco.ant" default="junit">
我們主要使用 JaCoCo 的兩個任務:首先是jacoco:coverage,用來生成單元測試覆蓋率數據,這是一個二進制文件,為了生成從該文件生成報表,我們還要調用另外一個任務jacoco:report,它的輸入為jacoco:coverage生成的二進制文件,輸出報表。報表有多種格式可選,可以是 HTML、XML、CSV 等。具體的腳本如下:
清單 9. 使用 JaCoCo 生成測試覆蓋率和報表
<jacoco:coverage destfile="${build.dir}/jacoco.exec"> <junit fork="true" forkmode="once" printsummary="yes"> <classpath> <path refid="classpath" /> <path location="${classes.dir}" /> <path location="${tests.dir}" /> </classpath> <batchtest fork="yes"> <fileset dir="${test.dir}" includes="**/*Test.java"/> </batchtest> </junit> </jacoco:coverage> <jacoco:report> <executiondata> <file file="${build.dir}/jacoco.exec"/> </executiondata> <structure name="dw demo"> <classfiles> <fileset dir="${classes.dir}"/> </classfiles> <sourcefiles encoding="UTF-8"> <fileset dir="${src.dir}"/> </sourcefiles> </structure> <html destdir="${build.dir}"/> </jacoco:report>
JaCoCo 的任務定義非常清晰,在這里略作說明。首先需要將原來的junit任務嵌入jacoco:coverage,而且需要指定fork="true",代表單元測試需要另起一個 JVM 執行,否則 JaCoCo 就會執行失敗。destfile="${build.dir}/jacoco.exec"指定生成的測試覆蓋率文件輸出到什么地方,后面生成報告的時候需要輸入該文件的地址。
然后就是使用 jacoco:report 生成報告,指定前面任務生成的單元測試覆蓋率文件、編譯好的類文件以及源代碼,最后選擇一種格式,這里使用 html,生成報告。打開報告的存放路徑,就可以看到如下所示的單元測試覆蓋率報告:
圖 5. HTML 版的單元測試覆蓋率報告
和同類產品比較
市面上流行的單元測試覆蓋率工具還有 Clover 和 Cobertura。和它們相比,JaCoCo 有如下優勢:
JaCoCo 擁有友好的授權形式。JaCoCo 使用了 Eclipse Public License,方便個人用戶和商業用戶使用。而 Clover 對于商業用戶是收費的。
JaCoCo 被良好地集成進各種工具中。在 Java 社區里,很多流行的工具都可以集成 JaCoCo,比如 SonarQube、Jenkins、Netbeans、Eclipse、IntelliJ IDEA、Gradle 等。
JaCoCo 社區非常活躍,它是目前唯一支持 Java 8 的單元測試覆蓋率工具。而且關于 JaCoCo 的文檔相對較多,降低了學習門檻。
感謝各位的閱讀!關于“怎么使用JaCoCo分析java單元測試覆蓋率”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。