Java 代碼覆蓋率工具 JaCoCo:實踐篇
上周 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