Java 代碼覆蓋率工具 JaCoCo:實踐篇

reai1096 8年前發布 | 33K 次閱讀 Java 項目構建工具 Java開發

上周 JAVA代碼覆蓋率工具JaCoCo-原理篇 簡單介紹了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) 不要盲目的為了提供覆蓋率而補充用例,應該想辦法設計更好的用例,哪怕多設計的用例對覆蓋率提升沒有效果。

讀者互動環節

覆蓋率的結果是否可以作為評估測試充分性的唯一標準?或者說有沒有其他手段一起來輔助評估,歡迎大家一起討論。

精彩的回答除了可以移入精選留言,還有機會獲取TMQ提供的精美禮品一份~ 期待您的回答哦~

長按指紋識別圖中的二維碼,獲取更多測試干貨分享!

將我們公眾號置頂 不會漏掉我們的原創干貨哦!

 

來自:http://mp.weixin.qq.com/s?__biz=MzIxNzEyMzIzOA==&mid=2652314603&idx=1&sn=e84c651a58f2fa9252c67d26b6454f38&scene=0

 

 本文由用戶 reai1096 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!