一.簡介
每種編程語言都有自己的單元測試框架。執行單元測試的工作一般由構建工具來完成。Jenk-ins做的只不過是執行這些構建工具的單元測試命令,然后對測試報告進行收集,並呈現。
Jenkins並不會自動幫我們寫單元測試,寫單元測試還是要靠人。為什么要這樣說呢?因為筆者發現,不少人認為Jenkins的自動化測試是指Jenkins代替人自動寫測試。
二.單元測試
JUnit
JUnit是一個Java語言的單元測試框架,由Kent Beck和ErichGamma創建。當執行maven test命令時,Maven會執行測試階段(包括單元測試),然后生成測試報告。
收集並展示JUnit測試報告的步驟如下:
1.安裝Jenkins JUnit插件,搜junit
2.在Jenkins中加入junit步驟,通常將步驟放在post always中,因為當測試不通過時,我們依然可以收集到測試報告
post {
always {
junit testResults: "**/target/surefire-reports/*.xml"
}
}
當pipeline運行結束后,在構建頁的左邊菜單欄及右邊詳情下都會多出一個鏈接:Test Result

3.單機“Test Result”進入,可以看到測試報告的詳細信息

junit步驟的testResults參數支持Ant風格路徑表達式。**/targetlsurefire-reports/*.xml表示只要是target/surefire-reports目錄下的XML文件就會被當作JUnit測試報告處理,而不論target在哪個層級的目錄下。
JacoCo
JUnit只是方便我們寫單元測試的一個框架,但是並沒有告訴我們有多少代碼被測試覆蓋到了。而JaCoCo填補了這一空白。JaCoCo是一個免費的Java代碼覆蓋率的庫,能幫助我們檢測出代碼覆蓋率,並輸出覆蓋率報告。
JaCoCo提供了以下幾個維度的覆蓋率分析。
- 指令覆蓋率( Instruction Coverage )
- 分支覆蓋率( Branch Coverage )
- 圈復雜度覆蓋率( Cyclomatic Complexity Coverage )
- 行覆蓋率( Line Coverage )
- 方法覆蓋率( Method Coverage )
- 類覆蓋率( Class Coverage )
以下是JaCoCo插件的使用步驟。
1.安裝JaCoCo插件,搜索jacoco
2.在Maven項目中引入JaCoCo插件,執行maven jacoco生成代碼覆蓋率報告
<plugin>
<groupId>org. jacoco</ groupId>
<artifactId>jacoco- maven- plugin</ artifactId>
<version>0.8.2</version>
<executions>
<execution>
<id>prepare- agent</id>
<goals>
<goal>prepare - agent</ goal>
</goals>
</execution>
<execution>
< id>report</id>
<phase>prepare - package</ phase>
<goals>
<goal>report</ goal>
</goals>
</execution>
<execution>
<id>post- unit - test</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<!-- jacoco執行數據的文件路徑- - >
<dataFile>target/jacoco. exec</dataFile>
<!--輸出報告的路徑-- >
<outputDirectory>target/jacoco- ut</outputDirectory>
</configuration>
</execution>
</executions>
<configuration>
<systemPropertyVariables>
<jacoco- agent . destfile>target/jacoco. exec</jacoco- agent . destfile>
</ systemPropertyVariables>
</configuration>
</p1ugin>
3.使用jacoco步驟,在mvn命令之后執行,寫法如下
steps{
sh "mvn clean install”
jacoco(
//代碼覆蓋率統計文件位置,Ant風格路徑表達式
execPattern: ' target/**/* . exec',
// classes 文件位置,Ant風格路徑表達式
classPattern: ' target/classes',
//源碼文件位置,Ant風格路徑表達式
sourcePattern: ' src/main/java',
//排除分析的位置,Ant風格路徑表達式
exclus ionPattern: 'src/test*',
//是否禁用每行覆蓋率的源文件顯示
skipCopyOfSrcFiles: false,
//如果為true,則對各維度的覆蓋率進行比較。如果任何一個維度的當前覆蓋率小於最小覆蓋率閾值,則構建狀態為失敗
//如果當前覆蓋率在最大閾值和最小閾值之間,則當前構建狀態為不穩定;如果當前覆蓋率大於最大閾值,則構建成功
changeBuildStatus: true,
//字節碼指令覆蓋率
minimumInstructionCoverage: ' 30' , maximumInstructionCoverage: '70',
//行覆蓋率
minimumLineCoverage: ' 30' ,maximumL ineCoverage: '70',
//圈復雜度覆蓋率
minimumComplexityCoverage: ' 30' ,maximumComplexityCoverage:'70',
//方法覆蓋率
minimumMethodCoverage: ' 30' , maximumMethodCoverage:'70',
//類覆蓋率
minimumClassCoverage: ' 30',maximumClassCoverage: ' 70',
//分支覆蓋率
minimumBranchCoverage: ' 30' ,maximumBranchCoverage:'70',
//如果為true,則只有所有維度的覆蓋率變化量的絕對值小於相應的變化量閾值時,構建結果才為成功
build0verBuild: true,
//以下是各個維度覆蓋率的變化量閾值
deltaInstructionCoverage: ' 80',deltaLineCoverage: '80',
deltaMethodCoverage: ' 80 ' ,deltaClassCoverage: '80',
deltaComplexityCoverage: ' 80',deltaBranchCoverage: '80 '
)
}
為了更好地理解jacoco步驟的參數,我們看看插件在自由風格項目中的UI

pipeline運行完成后,我們可以在任務詳情頁的下方看到報告。

buildOverBuild和changeBuildStatus參數都能影響Jenkins任務的結果狀態,那么當這兩個參數的值都為true時,結果由其共同決定
* Success AND Success = Success
* Unstable AND Unstable = Unstable
Failure AND Failure = Failure
X AND Failure = Failure, Failure AND X = Failure, X = Success/Unstable/Failure
* Y AND Unstable = Unstable, Unstable AND Y = Unstable, Y = Success/Unstable
最后,各個維度的覆蓋率應該設置多少呢?沒有標准答案。筆者的經驗是先要確定項目是遺留的還是新建的。遺留的就以當前覆蓋率為基線,新建的則設置相對高一些的要求。 再看項目的緊急程度,如果非常緊急的話,則可以考慮放低要求。最后看項目的重要程度,如果這個項目在整個架構中起着非常重要的作用,那么覆蓋率要求會高一些。
三.總結
代碼覆蓋率越高,軟件的質 量就越高嗎?
我們來看看《軟件之道:軟件開發爭議問題剖析》中是怎么說的:為了找到兩者之間的關系,把WindowsVista(4000多萬行代碼、幾千個二進制文件、數千名工程師)的分支覆蓋率( Branch Coverage )與塊覆蓋率( Block Coverage )和其發布六個月內的現場缺陷對應起來。我們觀察到覆蓋率和質量之間有弱正相關性,預測查准率和查全率較差(查准率為83.8% ,查全率為54.8% )。
書中最終給出的答案是:代碼覆蓋率最好不要單獨使用,而是需要與其他指標,如代碼變動率、復雜度等一並考慮。所以,如果考慮將代碼覆蓋率作為團隊開發人員的KPI,請慎重。
