1. 程式人生 > >JAVA程式碼覆蓋率工具JaCoCo-實踐篇

JAVA程式碼覆蓋率工具JaCoCo-實踐篇

一、覆蓋率專案中使用介紹

本節開始詳細介紹下專案中的JaCoCo實戰經驗。

下圖是覆蓋率在實際在專案中的主要實施點:

分別詳細介紹下:

1.1 確定插樁方式

Android專案只能使用JaCoCo的離線插樁方式。

為什麼?主要是因為Android覆蓋率的特殊性:

一般執行在伺服器java程式的插樁可以在載入class檔案進行,運用java Agent的機制,可以理解成”實時插樁”。JaCoCo提供了自己的Agent,完成插樁的同時,還提供了豐富的dump輸出機制,如File,Tcp Server,Tcp Client。覆蓋率資訊可以通過檔案或是Tcp的形式輸出。這樣外部程式可很方便隨時拿到被測程式的覆蓋率。

但是Android系統破壞了JaCoCo這種便利性,原因有兩個:

(1)Android虛擬機器不同與伺服器上的JVM,它所支援的位元組碼必須經過處理支援Android Dalvik等專用虛擬機器,所以插樁必須在處理之前完成,即離線插樁模式。(2)Android虛擬機器沒有配置JVM 配置項的機制,所以應用啟動時沒有機會直接配置dump輸出方式。

1.2 分析專案打包流程

專案目前還是已build方式打包,屬於Apache Ant方式。

插樁前先熟悉下專案build內容。

專案主要有幾個build檔案:

存放在根目錄下的build.xml檔案,這個是專案構建的組織檔案

.ant目錄下的build_common.xml,這個是構建時target內容。

.ant目錄下的build_option.xml,定義的屬性檔案。

.ant目錄下的build_plugins.xml,外掛檔案。

在插樁前,應該對專案構建過程做一個總體的熟悉,瞭解下每個target的作用,這樣才能確定不會影響各個插樁點,不會遺漏,否則會在打包的過程中出現各種各樣的問題。

1.3 程式碼插樁

http://eclemma.org/jacoco/trunk/doc/ant.html,這個地址是JaCoCo的ant的說明文件。

裡面簡單介紹了其支援的task型別,包括:

Task coverage、Task agent、Task merge、Task report、Task instrument、Task dump

具體怎麼使用可以參考裡面的例子。

各Task實際呼叫的類,看一下JaCoCo的antlib.xml就知道了

專案根據自己的情況暫時只用到了Task instrument,其他dump、merge、report是通過其他方式使用的,具體後面有說明。

為什麼沒有用到dump、merge、report?

這種情況比較適合一個帶有自動化測試的構建:打包、自動化測試、dump、merge、report。

專案部分功能需要手工測試,因此,上述幾個步驟需要後面再另外處理。

OK,簡單瞭解了JaCoCo的ant方式,下面開始對專案進行插樁打包。

專案的插樁修改步驟如下:

主要修改了build_common.xml和build_plugins.xml兩個檔案:

以下是build_common.xml的修改,build_plugins.xml的修改就不累述了,原理一樣。

(1) 檔案開頭的名稱空間加入

(2) 引入 JaCoCo 的 jar 和相關定義

(3) 重新定義 class 檔案生成路徑

(4) 修改compile編譯節點,插樁注入

(5) 修改打包package節點,主要是指定 JaCoCo 編譯後的類路徑

(6) 修改混淆obfuscate節點,增加混淆所需要的

將delete、mkdir、unzip操作指向classes_instr

(7) 修改分包splitClasses節點,指向classes_instr

(8) 修改熱補丁注入injectPatchCode節點,指向classes_instr

(9) 修改dex節點,指向classes_instr

(10) 修改dex_sub節點,指向classes_instr,同時在excludes中加入jacocoagent.jar

將上面的操作,做成全自動修改,打包成autoinsertxml.jar,放到打包伺服器後臺指定的目錄下。

Jar包裡詳細內容如下:

  • 修改AndroidManifest.xml檔案,增加一個覆蓋率生成服務(這個後續的覆蓋率生成工具用到)
  • 修改build_common.xml檔案,實現主幹程式碼插樁修改
  • 修改build_plugins.xml檔案,實現外掛程式碼的插樁修改

1.4 打覆蓋率包

Jekin上已經配置好了jacoco_package任務

按描述輸入後,直接點選開始構建就行了,打包後的結果:

包括:未插樁的主幹類檔案、未插樁的外掛類檔案、三種方式的覆蓋率包、mapping檔案等等。

jacoco_package任務裡面的具體內容做了什麼?一起看看吧。

(1) 配置了引數化構建的內容

(2) 配置了構建描述

(3) 配置了專案ID和建立精準入庫任務

(4) Check out程式碼

(5) 插樁

(6) 編譯打包

(7) 備份class

(8)儲存存檔檔案

1.5 執行測試,收集覆蓋率結果檔案

覆蓋率檔案生成現在支援兩種方式:

(1)覆蓋率生成工具:一個專門用來生成覆蓋率檔案的APK。(2)定時器的方式:在專案裡新建一個定時器JOB任務,定時去收集生成覆蓋率檔案。

目前我們主要用第一種方式,下面都詳細介紹下。

1.5.1 AndroidManifest檔案的修改

增加了兩個服務:

ResultManagerService:執行生成覆蓋率資料。

ReSetManagerService:執行清理覆蓋率資料。

1.5.2 生成覆蓋率的apk工具和jacoco-cov-sdk.jar包

 

工具總共有三個功能:

(1)生成ec檔案(2)啟動定時器,按指定的時間生成ec檔案(3)清除覆蓋率,會清除記憶體記錄並且會刪除sd卡存在的ec檔案

工具原理:

(1) 生成ec檔案

當觸發這個操作的時候,其實會去啟動專案中我們新增的ResultManagerService服務,它具體做的事情就是dump覆蓋率資料,如下:

在ResultManagerService啟動時呼叫jacoco-cov-sdk.jar包中的

ResultManager.dumpCoverageJacoco(true,filename)方法:

其主要功能就是反射呼叫jaCoCo的dump方法,來生成覆蓋率資料,核心程式碼如下:

(2) 啟動定時器,按指定的時間生成ec檔案

這個就是一個Timer,按指定的時間週期去dump覆蓋率資料

(3) 清除覆蓋率,會清除記憶體記錄並且會刪除sd卡存在的ec檔案

當觸發這個操作的時候,其實會去啟動專案中我們新增的ReSetManagerService服務,它具體做的事情就是reset覆蓋率資料,如下:

在ReSetManagerService啟動時呼叫jacoco-cov-sdk.jar包中的

ResultManager.reSetCoverageJacoco()方法:

其主要功能就是反射呼叫jaCoCo的reset方法,來清理覆蓋率資料,核心程式碼如下:

1.6 生成覆蓋率報告

通過編寫report的build方式來生成報告結果。

這裡寫了一個生成報告的模版,使用者只需要copy到 本機上,按下面的說明修改、生成報告即可,下面詳細介紹下這個模版的使用方法。

1.6.1 模版目錄介紹

(1) libs存放幾個jar包,分別為ant-contrib.jar、jacocoagent.jar、jacocoant.jar。(2) result_xml目錄會自動生成xml格式的報告。(3) src目錄是存放原始碼的,如果沒這個,生成的覆蓋率只有資料,看不到程式碼實際覆蓋的內容。(4) build檔案,ant的執行內容為build_group或者是build_only。

說明:這個build檔案需要根據實際的專案修改,修改一次以後基本不用變動。

(1) build_group檔案,指定組生成的build檔案,適合結果按組顯示。(2) build_only檔案,沒組的概念。(3) build_property檔案,存放的是ec列表檔名稱。

build檔案內容可以根據官方的demo參考。

1.6.2 實際操作舉例

1、安裝Apache Ant

網上下載或直接copy其他人機器上的就OK,設定下環境變數ANT_HOME和把bin目錄放到path中,我用的是apache-ant-1.9.6-bin,有需要可以直接找我要。

2、取上面的模版目錄放到你本機上(PC),有需要可以直接找我要。

3、生成報告,按以下步驟操作

比如拿到測試結果的ec檔案有三個,分別是yyb1.ec、yyb2.ec、yyb3.ec

(1) 將覆蓋率打包結果中的classes.zip丟到模版根目錄中並解壓。

(2) 根據打包時的svn地址和版本號,取下原始碼放如到src目錄。

(3) 將ec檔案(yyb1.ec、yyb2.ec、yyb3.ec)全部丟到模版根目錄中

(4) 修改build_property檔案,名稱寫如到value中(去掉ec字尾的)

(5) build檔案,如無路徑變化,基本不用修改

省電管理除了主幹程式碼,還有外掛部分,因此build檔案取的build_group,分別為<group name=”YYB”>、<group name=”plugin_power_save”>

(6) 執行ant,report目錄就會生成。

(7) report目錄生成後,進去執行index就看到覆蓋率報告。

省電管理的覆蓋率生成結果:

開啟index後的結果,按build檔案指定的分組生成了:

點選連結進入到實際程式碼中就可以實際覆蓋的結果了。

這裡有個注意的地方,如果想看到實際程式碼的覆蓋率,編譯的時候debug=”true” 這個一定要設定。

1.7 分析覆蓋率結果

網上關於JaCoCo覆蓋率報告的分析有不少的文章可以學習。

這裡闡明幾個自己的觀點:

根據專案的不同,在分析結果前先應該明確幾個事情,包括

(1) 確定改動點的範圍,根據這個範圍才會有針對性的做分析。(2) 改動點是否影響功能邏輯,如果不影響可以忽略。(3) 改動點和其他功能是否存在耦合,如果存在,耦合的部分也要做分析。

我們主要從上面幾點來分析覆蓋率,查漏補缺,這些改動點大部分已經覆蓋到了,基本認為應用的主要功能覆蓋完全,當然也不是完全絕對,在測試過程中結合FreeTest、探索性測試等手段也是一種不錯的選擇,切記不要盲目的為了覆蓋率而覆蓋,覆蓋率高不代表你真的覆蓋完全了。

分析過程很多人覺得是比較痛苦的,不妨可以把這個過程當作是一種鍛鍊,前面的一切都只是一個鋪墊,最最關鍵的在於分析階段,一個出色的分析結果可以達到事半功倍的效果。

我們的方法是任務已tapd提單的方式建立,按照模版,附上需求連結地址、svn地址和改動範圍、附件接受未插樁的class檔案、測試後的ec檔案等,分析出結果需要有分析過程、測試補充建議、分析耗時等等。

主要列出未覆蓋場景、冗餘方法、測試補充建議等等。

舉個分析的例子:

需求: 訊息盒子增量測試完成,進行覆蓋率分析。

1.7.1 熟悉需求用例

(1) 確認程式碼範圍根據需求,確定開發修改的程式碼範圍(2) 覆蓋率報告分析根據開發修改的程式碼範圍,對覆蓋率報告結果進行分析(3) 確認未覆蓋原因找出未覆蓋的部分,判斷是否需要覆蓋(4) 輸出測試策略

根據分析後的結果輸出再次出測試策略

(5) 補充測試驗證

根據策略補充相應的測試,再生成覆蓋率,和前一次做對比,最終達到功能大部分覆蓋。

二、覆蓋率與BVT測試結合

通過兩者的結合,可以得到每個BVT的用例的覆蓋率資料,可以得出幾個緯度的結果:

(1)用例和程式碼的對應關係用例和程式碼的動態對映關係,可能會存在對映到的函式比較多,作者建議根據功能有針對的篩選出重點函式來做對映。(2)上面對映關係彙總後,可以按方法的呼叫頻繁度來優化我們的程式碼,優化呼叫頻繁度高的程式碼,找出冗餘程式碼等等。

下面介紹下整個過程:

2.1 在BVT用例框架中插入覆蓋率方法

核心:找出關鍵點插入我們的覆蓋率方法

(1) 在每個用例執行前,插入清理覆蓋率資料的方法

在BVT基類的setUp()方法最後插入清理覆蓋率資料的方法。

這樣每個用例開始執行前,就會把以前遺留的覆蓋率資料清除掉,保證每次覆蓋率都是一條用例的執行結果。

(2) 在每個用例執行後,tearDown()方法中呼叫dump出覆蓋率資料。

dump出來的資料用例執行過程中真實的覆蓋率情況。

2.2 執行BVT用例,得到覆蓋率

執行BVN的用例,用例執行成後輸出覆蓋率檔案,一條用例對應一個覆蓋率檔案

2.3 批量生成覆蓋率報告,解析入庫

批量生成覆蓋率報告,根據用例和報告對應關係做批量入庫。

2.4 分析覆蓋率結果,得出用例和程式碼對映關係

上面我們已經得出每一個BVT用例的覆蓋率資料,對每一個覆蓋率資料結果進行分析,得出幾個緯度的資料,用例->包->類->方法的覆蓋資料,這樣每個用例和程式碼的對映關係就出來了。

然後根據用例對應功能的特點,再篩選出重點方法,形成一個比較精簡的用例和程式碼對映關係出來,方便我們後續的改動點定位。

三、差異覆蓋率和全量覆蓋率

測試完後,根據覆蓋率結果衡量測試覆蓋程度,主要分為兩種:

(1) 差異覆蓋率:改動點的程式碼執行覆蓋率情況(2) 全量覆蓋率:本次測試程式碼執行全部覆蓋率情況

使用哪種覆蓋率是由測試階段的內容決定,比如上線前測試、整合或合流階段,主要關注的是改動點的變化,使用差異覆蓋率效果比較理想。如果是新增功能,使用全量覆蓋率比較理想。

3.1 差異覆蓋率

差異覆蓋率主要是根據開發程式碼變更的diff差異,得出改動程式碼的範圍,然後根據這個範圍有針對性的只生成這部分改動的程式碼覆蓋率結果。

通過覆蓋率結果反向衡量測試的充分性,更好的和精準評估的測試範圍去做比較。

3.2 全量覆蓋率

全量覆蓋率即全部程式碼的覆蓋結果,不一定要全部去分析,只需關注改動部分及其耦合功能的覆蓋情況即可,這裡結合精準耦合分析結果一起分析。

四、衡量覆蓋率結果

程式碼覆蓋是一種狀態指示器,而不是衡量效能或正確性的單元。

程式碼覆蓋率是給程式設計師參考的,是給我們發現程式碼中問題的一種手段,可以發現過時的,未測試的類,還可以發現未經測試執行可能導致問題的路徑。在實際專案中,程式碼覆蓋率總是低於100%。取得完全覆蓋是不可能的,如果取得,那也是非常罕見的。分析前一定要確定那些為必須覆蓋,那些為可以或不覆蓋,不要為了覆蓋而覆蓋,程式碼邏輯的熟練程度對分析覆蓋率會有很大的幫助,一定要先梳理清楚。

五、本章小結

程式碼覆蓋率是軟體測試中的一種度量手段,主要用來描述程式中原始碼被測試的比例和程度。

在單元和系統測試過程中,其常常被拿來作為衡量測試好壞的指標,甚至很多情況下用程式碼覆蓋率來考核測試任務完成情況,經常會被要求程式碼覆蓋率必須達到XX%以上,才算測試充分,於是乎測試人員或者開發人員費盡心思設計案例來覆蓋程式碼,這種用程式碼覆蓋率來衡量,有利也有弊。

給夥伴們的一些忠告:

(1) 覆蓋率資料只能代表你測試過哪些程式碼,不能代表你測好這些程式碼。(2) 不要過於相信覆蓋率資料。(3) 不要只拿語句/行覆蓋來衡量。(4) 路徑覆蓋率>判斷覆蓋>語句覆蓋。(5) 不要盲目的為了提供覆蓋率而補充用例,應該想辦法設計更好的用例,哪怕多設計的用例對覆蓋率提升沒有效果。