JaCoCo Espresso Unit Test

簡介如何利用JaCoCo取得Android專案的覆蓋率報告

莊以楷 2021/01/13 14:54:01
4230

簡介如何利用JaCoCo取得Android專案的覆蓋率報告

 

作者YiKai Chuang

 

前言

 

JaCoCo是Java Code Coverage的縮寫,它可以跟Ant、Maven或Jenkins整合,快速找出專案的原始程式碼有哪些地方沒有被Unit Test(單元測試)所覆蓋,本文是基於Android專案的androidTest資料夾裡頭已經有寫好Espresso的單元測試程式碼的前提下,對於如何套用JaCoCo,讓Android專案執行完Espresso測試的同時,可以產出test coverage report(覆蓋率報告),做一個概括性的介紹。

 

在Android專案串接JaCoCo

 

  1. 首先,需要修改app層級的build.gradle:加上apply plugin: 'jacoco'這行,因為Android Plugin for Gradle從0.10.0版開始支援了JaCoCo,所以只要在build.gradle的buildTypes裡宣告testCoverageEnabled = true這行,就可以利用JaCoCo外掛,做簡單的覆蓋率測試。另外,因為在build.gradle的buildTypes的debug內宣告testCoverageEnabled = true,所以之後會有createDebugCoverageReport的task可以用

 

  1. 有些文章提到產出覆蓋率報告時需要在AndroidManifest.xml加上允許檔案讀寫的權限(譬如:<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />),測試之後發現即使不加,應該也不會有什麼影響

 

  1. 點擊Android Studio的Gradle,找到專案名稱底下的Tasks的verification資料夾,連續點擊connectedAndroidTest(或connectedCheckconnectedAndroidDebugTestcreateDebugCoverageReport)兩次之後,便會開始在手機上執行Espresso的單元測試

 

  1. 以連續點擊createDebugCoverageReport兩次為例當手機執行完Espresso的單元測試之後,Android Studio會出現BUILD SUCCESSFUL和Task execution finished 'createDebugCoverageReport'的提示訊息

 

  1. 以我個人的習慣,會先確認執行Espresso測試是否完全沒問題,所以通常都先看build資料夾的reports資料夾的androidTests資料夾的報告



  1. 若測試過程一切正常點擊index.html之後,會看到類似這樣的產出報告

 

  1. 接著開啟build資料夾的reports資料夾的coverage資料夾,就可以看到JaCoCo產出的覆蓋率報告

 

JaCoCo覆蓋率報告的簡介:

 

  1. JaCoCo包含了各種覆蓋率的計數器,譬如:Instructions coverage(執行的最小單位)、Branches coverage(值得注意的一點是:異常處理不列入計算)、Cyclomatic Complexity(簡單來說就是為了覆蓋所有路徑所需執行的單元測試的數量,若數值越大,表示原始程式碼可能越難測試和維護)、Lines(計算被測試程式的每行程式碼是否被執行)、Non-abstract methods(計算被測試程式的非抽象方法是否被執行)、Classes(計算被測試程式的class類檔案是否被執行

 

  1. 橫條的紅色部份表示單元測試尚未覆蓋的比重,橫條的綠色部分表示單元測試已經覆蓋的比重,若橫條的長度越長,表示該Element在整份報告中所佔的權重越高

 

  1. 以最底下Total旁邊的數值為例,984是目前尚未覆蓋的指令數,1632是全部的指令數,因此覆蓋率(coverage) = (1632-984) / 1632 = 39.7%



實作後的心得分享:

 

因為做某客戶的案子才第一次接觸JaCoCo,過程當中的感想、心得是

  1. 專案的原始程式碼寫得越模組化,在做單元測試時就會越輕鬆

  2. 也許是經驗不足,這次在產出JaCoCo報告時遇到的最大困難,就是原本想過濾掉跟Data Binding有關的程式碼,讓它不要出現在JaCoCo覆蓋率報告裡面。雖然參考過很多篇文章,也在build.gradle將Data Binding相關的packag name、檔名...等加到排除的條件,但就是一直無法出現預期的結果

 

 

(圖片引用自https://github.com/nongdenchet/android-data-binding/blob/master/jacoco.gradle)

  1. 若想提升JaCoCo報告的整體覆蓋率,建議找權重比較高的優先處理,所謂「擒賊先擒王」,同樣也適用於單元測試



最後總結:

 

這次參與的專案是等到Android主要程式都完成之後,才開始補做單元測試。然而,透過這次的經驗,也親身體驗到,若能在專案一開始,邊寫程式的同時,就邊寫各種不同邊界條件(boundary case)的單元測試程式碼,測試Android程式各個function的邏輯是否正確?是否有潛藏的漏洞(譬如:計算數值時除數在某些情況下有可能為0)?...等,諸如此類的單元測試的case越多,相信越有助於讓原始程式碼變得更為強健(robust),進而提升整個專案的軟體品質

 

參考來源:

 

  1. https://www.jianshu.com/p/671fad23c2ce

  2. https://jeremykao.gitbooks.io/learning-jacoco/content/android/

  3. https://yuweiguocn.github.io/android-unit-test-3/

  4. https://www.itread01.com/content/1588838824.html

莊以楷