2017/6/12

Espresso 只做了半套的 Code Coverage

有在使用 Espresso 撰寫測試程式的人應該都知道,在 Android Studio 中 Android Test 類型的 Configuration 是不能使用 Code Coverage 的,最少在 Android Studio 2.2.2 仍然是如此。也就是說「Run 'xxx' with Coverage」的按鈕沒有辦法按,情況如下圖所示:


這點和 JUnit 的 Configuration 不同:



產出 Coverage Report 的第一步

以上的限制,對認真撰寫測試程式的人來說,會造成很大的不便。Code Coverage 是撰寫測試程式的一項重要指標,沒有了 Code Coverage 就如同在伸手不見五指的黑暗中行走,完全不知身在何處,有關更多 Code Coverage 用途的細節,請參考先前的這篇文章

不過,說是做了半套,代表並不是完全沒有辦法產生 Coverage Report。要在 Espresso 測試執行的過程中產生 Coverage Report,要先調整 build.gradle,在 buildTypes 設定中開啟 testCoverageEnabled 選項。

完成以上修改,並執行 ./gradlew createDebugCoverageReport 指令成功之後,就可以在 Module 的 /build/reports/coverage/debug/ 路徑下,以 Browser 開啟 index.html

以上的步驟在搜尋之後,都可以取到一堆的文件提供相關的操作細節。但本篇到這裡還沒有結束,這個方法僅適用於測試和待測對象在同一個 Module 中。如果是在不同的 Module 裡,由 Coverage 的 Report 頁面中,其實可以看出清單中的 Package 都只列出測試程式所在 Module 的 Class。


擴增 Coverage Report 的範圍

假設有一個如下所示的專案結構:
+ Sample
  + app
    + src
      + androidTest
      + main
        + com.sample.app
      + test
  + domain
    + src
      + main
        + com.sample.domain

其中 app 是 Android Application,domain 是 Android Library。如果把所有的測試 Class 放在 app 的 Module 之下,在 test 路徑下的測試可經由 Configuration 的 Code Coverage 畫面,來把 domain 中的 Class 列入 Report 產生的範圍。


androidTest 中的測試就沒有這麼直接了,以 Android 的 Gradle Plugin 官方文件內容來看,目前並沒有相關可調整的參數。所以這個階段的目標,就是讓 androidTest 產出的 Report 和 test 一樣,可以把指定位置的 Class 列入 Coverage 的範圍內。

要達到這個目標,首先要引入 JaCoCo 的 Plugin,透過 JaCoCo 的參數調整來突破原本的限制。在 JaCoCo 中有 sourceDirectoriesclassDirectories 二項參數,用來指定 Report 要涵蓋的範圍。以上面專案結構的例子來說,需要產生以下的內容在 app 的 build.gradle 中:

在這裡附帶說明一下,其實可以把以上的內容獨立成單獨的檔案,例如:jacoco.gradle,再透過 apply from: 'jacoco.gradle' 的方式引用。一來可以簡化 build.gradle 的內容、方便維護,二來可以避免調整 JaCoCo 的選項時,IDE 頻繁地出現要求同步的訊息。

另外有一點需要注意的是,createDebugCoverageReport 產出的 executionData 是以 *.ec 為名稱,與一般 JaCoCo 使用的 *.exec 不同。

設定好以上的內容之後,就可以執行 ./gradlew app:jacocoTestReport。和 createDebugCoverageReport 一樣會產出 html 格式的 Report,但不同的是 index.html 會在 app/build/reports/jacoco/jacocoTestReport/html 路徑下看到。這個路徑是預設的,可以透過參數調整。


修正 Coverage Report 的問題

到這裡所有的問題都解決了嗎?其實並沒有!照著以上的內容所產出的 Report 中,domain 下的 Class 不論測試怎麼做,在 Instruction 和 Branch 的 Coverage 都呈現 0%,所以顯然有一個環節出現了問題。

花了一番功夫發現 domain 在執行 jacocoTestReport 時都只會產生 release 的 Class。

就算是把 classDirectories 的路徑調整成 release,在產出的 Report 中 Coverage 依然是 0%。

目前解決方案是要調整 domain 的引用方式:

而在 domain 的 defaultConfig 下也要增加以下的設定:

使用以上內容再執行一次 ./gradlew app:jacocoTestReport,就可以由 Report 中看到,domain 下的 Class 不再全都是 0% 的狀態。


仍需改善的部份

雖然 Report 已經可以順利的產出,但是與 JUnit 可以在 IDE 中直接檢視 Coverage 情況的經驗相比,還是有差別。畢竟沒有辦法直接在 IDE 對照著 Code 就可以了解 Coverage 的情況,要在 IDE 與 Browser 間來回地切換其實非常地不方便。

原本有試著把 createDebugCoverageReport 產出的 executionData,經由以下【Analyze -> Show Coverage Data...】功能所顯示的 Window 載入。但這個功能似乎只能載入 *.exec 的檔案,就算是把 *.ec 改為 *.exec,載入之後 Coverage 的狀態也都呈現 0% 的結果。可能是真的有格式上相容的問題,所以才會把產出的 executionData 以 *.ec 來區格,避免被 Android Studio 載入。
只能期待未來 Android Studio 在改版時,能將這一部份的功能納進入,既然在 Plugin 都已經可以產出 Report 了,把 Plugin 的動作整合到 IDE 的 Coverage 按鈕上,應該不是什麼難事吧!
















0 意見:

張貼留言