JVM插码之六:jacoco插码及问题“$jacocodata 属性 Method not found: is$jacocoData”
在使用jacoco統(tǒng)計自動化代碼覆蓋率
jacoco統(tǒng)計自動化代碼覆蓋率
1. 簡介
1.1. 什么是Jacoco
Jacoco是一個開源的代碼覆蓋率工具,可以嵌入到Ant 、Maven中,并提供了EclEmma Eclipse插件,也可以使用JavaAgent技術(shù)監(jiān)控Java程序。很多第三方的工具提供了對Jacoco的集成,如sonar、Jenkins等。
1.2. 什么是代碼覆蓋率
代碼覆蓋(Code coverage)是軟件測試中的一種度量,描述程式中源代碼被測試的比例和程度,所得比例稱為代碼覆蓋率。
代碼覆蓋率是衡量測試質(zhì)量的一個重要指標(biāo)。在對一個軟件產(chǎn)品進行了單元測試、組裝測試、集成測試以及接口測試等繁多的測試之后,我們能不能就此對軟件的質(zhì)量產(chǎn)生一定的信心呢?這就需要我們對測試的質(zhì)量進行考察。如果測試僅覆蓋了代碼的一小部分,那么不管我們寫了多少測試用例,我們也不能相信軟件質(zhì)量是有保證的。相反,如果測試覆蓋到了軟件的絕大部分代碼,我們就能對軟件的質(zhì)量有一個合理的信心。
代碼覆蓋分為下面五種情況:
1.2.1. 函數(shù)覆蓋
函數(shù)覆蓋(Function Coverage),執(zhí)行到程序中的每一個函數(shù)(或副程式)。
1.2.2. 語句覆蓋
語句覆蓋(Statement Coverage),又稱行覆蓋(Line Coverage),段覆蓋(Segment Coverage),基本塊覆蓋(Basic Block Coverage),這是最常用也是最常見的一種覆蓋方式,就是度量被測代碼中每個可執(zhí)行語句是否被執(zhí)行到了。這里說的是“可執(zhí)行語句”,因此就不會包括像C++的頭文件聲明,代碼注釋,空行,等等。非常好理解,只統(tǒng)計能夠執(zhí)行的代碼被執(zhí)行了多少行。需要注意的是,單獨一行的花括號{}也常常被統(tǒng)計進去。語句覆蓋常常被人指責(zé)為“最弱的覆蓋”,它只管覆蓋代碼中的執(zhí)行語句,卻不考慮各種分支的組合等等。假如你的上司只要求你達到語句覆蓋,那么你可以省下很多功夫,但是,換來的確實測試效果的不明顯,很難更多地發(fā)現(xiàn)代碼中的問題。
1.2.3. 判斷覆蓋
判斷覆蓋(Decision Coverage),又稱分支覆蓋(Branch Coverage),所有邊界覆蓋(All-Edges Coverage),基本路徑覆蓋(Basic Path Coverage),判定路徑覆蓋(Decision-Decision-Path)。它度量程序中每一個判定的分支是否都被測試到了。這句話是需要進一步理解的,應(yīng)該非常容易和下面說到的條件覆蓋混淆。因此我們直接介紹第三種覆蓋方式,然后和判定覆蓋一起來對比,就明白兩者是怎么回事了。
1.2.4. 條件覆蓋
條件覆蓋(Condition Coverage),它度量判定中的每個子表達式結(jié)果true和false是否被測試到了。
1.2.5. 路徑覆蓋
路徑覆蓋(Path Coverage),又稱斷言覆蓋(Predicate Coverage)。它度量了是否函數(shù)的每一個分支都被執(zhí)行了。 這句話也非常好理解,就是所有可能的分支都執(zhí)行一遍,有多個分支嵌套時,需要對多個分支進行排列組合,可想而知,測試路徑隨著分支的數(shù)量指數(shù)級別增加。
1.3. Jacoco的功能
1.3.1. 覆蓋率計數(shù)器
Jacoco使用一系列的不同的計數(shù)器來做覆蓋率的度量計算。所有這些計數(shù)器都是從java的class文件中獲取信息,這些class文件可以(可選)包含調(diào)試的信息在里面。即使在沒有源碼的情況下,這種方法也可以實時有效地對應(yīng)用程序進行度量和分析。在大部分情況下,收集到的信息可以映射到源碼,可視化到每一行代碼的粒度。但這種方法還是有一些限制。這些class文件必須使用調(diào)試信息來編譯,這樣才可以計算行的覆蓋率和提供出源碼的高亮。但不是所有的JAVA語言的結(jié)構(gòu)都可以直接編譯成一致的二進制代碼。在這種情況下,java 編譯器會創(chuàng)建所謂的“合成”代碼,會導(dǎo)致產(chǎn)生一些不期望得到的覆蓋率結(jié)果。
1.3.2. 指令覆蓋率
Jacoco最小的計數(shù)單元是單個java二進制代碼指令。指令覆蓋率提供了代碼是否被執(zhí)行的信息。這個度量完全獨立源碼格式,并且總是可用,即使class文件里面沒有調(diào)試信息。
1.3.3. 分支覆蓋率
Jacoco也計算分支的覆蓋率,包括所有的if和switch語句。這個度量計算一個方法里面的總分支數(shù),確定執(zhí)行和不執(zhí)行的分支數(shù)量。分支覆蓋率總是可用的,即使class文件里面沒有調(diào)試信息。注意異常處理是不在分支度量里面統(tǒng)計的。
2、部署
自動化測試接口時,利用jacoco的agent統(tǒng)計覆蓋率,部署方法是在jvm啟動命令行里增加如下命令:
-Djava.awt.headless=true -javaagent:D:\AutoTest\jacoco\lib\jacocoagent.jar=includes=com.hundsun.*,output=tcpserver,port=8229,address=127.0.0.1 -Xverify:none問題:
org.springframework.data.mongodb.UncategorizedMongoDbException: Query failed with error code 2 and error message 'unknown top level operator: $jacocoData'?
?原因是,封裝的mongo的API里利用反射對查詢參數(shù)的自動化拼接查詢語句,在啟動了jacoco后,dto里插碼了jacocoData屬性,被反射組裝成查詢語句了。
解決辦法:
后來問了部署jacoco服務(wù)的框架組人員,發(fā)現(xiàn)是用了java Agent在修改運行時字節(jié)碼實現(xiàn)的,拉了dump發(fā)現(xiàn)確實在運行時進行字節(jié)碼修改的,而在jacoco官方的github也曾經(jīng)有過這個問題的issue:
jacocoData
即使用了是否為復(fù)合字段的field方法解決。即在反射的組裝位置通過isSynthetic的判斷如果為false,說明是插碼的屬性,不處理即可。
總結(jié)
以上是生活随笔為你收集整理的JVM插码之六:jacoco插码及问题“$jacocodata 属性 Method not found: is$jacocoData”的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JVM插码之五:Java agent+A
- 下一篇: 实战渗透 | 向吃鸡外挂站开炮