精準測試系列分享之一:JaCoCo 企業級應用的優缺點分析

星雲精準測試發表於2021-11-01

一、JaCoCo簡介

JaCoCo是Eclipse平臺下的開源產品,以小型,輕量化著稱,常見整合在Eclipse Workbench中,除此之外的啟動方式包括對接Ant和Maven,或是命令列的方式進行。Jacoco近兩年在軟體測試行業的被關注度比較高,其主要原因是:在新一代精準測試技術流的影響中,各大型單位對覆蓋率的追求越來越迫切。作為一款開源產品,它主機面向Java語言,能夠在位元組碼層面給出覆蓋率,同時也能將位元組碼關聯到對應的原始碼。這種半精準的測試方式,在小型團隊中,對於某些場景的覆蓋率訴求,起到了一定的響應。但它也有很強的侷限性,尤其在支撐大型系統應用中,其表現能力弱,準確率不夠達標。這是它致命的缺點,也為它在大型系統中應用的 失敗埋下了伏筆。如何下此結論?本文作者將從應用者的角度,並參照國外使用者的總結文獻做一綜合分析羅列。因本人的學識所限,歸納的肯定不夠全面,如有不準確之處,還請同行拍磚、指正。

二、JaCoCo的技術核心

JaCoCo覆蓋率的採集,主要是通過插裝及裝點的執行來收集程式執行的特徵資料。JaCoCo的插裝,是通過agent在位元組碼中插入boolean[]陣列,來標記每句可執行程式碼,只要執行過相應語句,boolean[]陣列會產生相應標記(True or False),這個boolean陣列連同產生的標記稱之為探針(Probe)。

當原始碼被編譯為位元組碼時,原始碼的邏輯指令,例如條件判定,迴圈等,會被編譯為特殊指令,JaCoCo在動態插裝的過程中,當處理到這些指令時,會根據不同的邏輯進行不同的插裝方式。

圖表 1 Java原始碼(左)和對應位元組碼 (右)

圖1中IFEQ和G0T0都是JUMP(跳轉)指令,通過JUMP指令直接能從一個節點,跳轉到另一個節點。針對JUMP指令的插裝方式,有多種選擇。有些是在指令前插入探針,有些是在指令後插入探針(見表格1)。類似的,在面對其他特殊指令時,例如ENTRY(程式啟動), SEQUENCE(序列執行),或者EXIT(程式終止)等,均有不同的插裝操作。圖2以面對JUMP型別指令的插裝作為示例,其中左圖為未插裝的位元組碼,右圖為裝點位置的示意圖。

 

圖表 2針對JUMP型別指令的插裝示意

表格 1面對不同型別指令的插裝方式

JaCoCo的這種插裝方式,特別需要指出的是:

  • 看似針對不同型別的指令,通過不同的插裝方式,反映了不同的邏輯關係,但由於其對整體的結構沒有把控,當程式較為複雜時,十分容易產生誤差,並且難以反映真實的呼叫關係。
  • 除了表1列出的幾種指令外,JaCoCo對於其他的指令視為Exception,並不予處理,整個插裝過程,以及插裝位置對於使用者而言均不可見,無法通過人工干預合理和調整插裝問題,導致即使使用者有排查問題的需求和能力,也無從有效研究問題的根源。
  • 這種無序的混亂插裝方式,也造成不必要的效能開銷,比如:使用JaCoCo會使程式碼的膨脹率增加約30%,並增加約10%的效能消耗。(由於JaCoCo的設計初衷是針對民用專案,因此優化程度較低,當專案規模較小時,其影響並不顯著,但是對於資源十分寶貴的商業專案,會產生較大的負荷。)

三、JaCoCo的覆蓋率實現

比較常見的覆蓋率標準,按粒度從粗到細依次是:語句覆蓋,分支覆蓋,條件覆蓋,路徑覆蓋。JaCoCo可以實現語句覆蓋,以及部分情況下的分支覆蓋。一些研究表明,JaCoCo在分支覆蓋層級,對結構控制只研究了if-else的情況,因此該層級的評估無法保證所有可執行語句的正確執行[1]。同時,JaCoCo所謂支援的條件覆蓋結果其實不適合商用,由於位元組碼和原始碼之間的資訊差,它只能衡量條件真假是否覆蓋的比率,但無法有效確定某個具體條件執行的精確結果。而在企業級應用場景下,實現精確的條件覆蓋是剛性需求,JaCoCo因為沒有程式碼靜態分析輔助永遠無法純粹通過位元組碼獲取精確的條件和分支覆蓋。

因此,JaCoCo優點是,可以進行粗粒度的覆蓋率度量,比較適用於對於專案的整體初步評估。

在真正的精準測試的體系中,對於覆蓋率的計算和展示,其實現基礎是測試資訊到原始碼,再從原始碼到測試用例的精準關聯和資訊對映。與其相比,Jacoco最大的弱點是:易產生覆蓋率的計算偏差。

  • JaCoCo原生不支援和測試用例的關聯。因此只能在手動執行測試用例後,給出當前測試用例所對應的整體覆蓋率資訊。受限於設計理念,JaCoCo的覆蓋率是在位元組碼層級統計,為了使用者的可讀性和可研究性,需要和原始碼進行關聯,關聯的過程通過一種對映機制(mapping)來實現,而在這種對映的過程中,會損失大量的資訊,因此,會進一步造成覆蓋率資訊的誤差。
  • 在覆蓋率的應用端,商業適配性較差。第一,JaCoCo不支援多專案並行或者多版本累計的覆蓋率統計,其覆蓋率只針對某個用例對於某個專案的評估。JaCoCo的理念更偏向於傳統白盒測試工具,在敏捷迭代的場景下,JaCoCo無法對版本/輪次/里程碑版本/專案之間的差異或者總體覆蓋率計算進行支援。第二,JaCoCo原生不支援真正意義上的精準測試,其內部資料表達都是聚合的陣列,不支援區分不同執行緒的覆蓋率統計。第三,JaCoCo不支援實時的覆蓋率展示,其覆蓋率結果只能在該測試任務結束後生成。第四,JaCoCo不支援結果的自動儲存和累計,一旦退出環境,所有未經儲存的結果都會被清空。
  • 對於程式的邏輯結構和呼叫關係,JaCoCo除了在動態插裝的過程中進行實時分析以外,並無其他的獲取方式。而對於覆蓋率的JaCoCo能夠在Java檔案的基礎結構層級的基礎上,從四類資料:覆蓋率,被覆蓋行數,未覆蓋行數,以及總行數,實現覆蓋程度的評估和展示。其介面如圖3所示。同時,在實現原始碼和位元組碼的對映後,統計的覆蓋率資訊也會相應的在原始碼端的程式碼行上進行展示,介面如圖4所示,其中綠色代表完全覆蓋,黃色代表部分覆蓋(一些指令或者分支未覆蓋),紅色代表未覆蓋,這些顏色也支援使用者自定義。針對覆蓋結果的匯出,JaCoCo支援HTML,XML,CSV等檔案格式,使用者也可以在Eclipse內進行檢視。

圖表 3 JaCoCo覆蓋率展示介面

圖表 4 JaCoCo在原始碼端的覆蓋率展示

四、應用侷限性

JaCoCo天生缺陷的核心設計和定位,帶來了應用的侷限。作為一款熱門開源工具,雖然被愛好者們在小範圍及初步資訊判斷中使用較多,但是卻因為大量的誤差,不能被應用更大、更復雜的大型核心系統上,否則使用者可能將面臨著很大的技術風險。

覆蓋率誤差:JaCoCo的插裝過程具有一定的技術侷限性,其裝點並非明文可見,存在大量的資訊差。發表在IEEE上的文章:“Negative effects of bytecode instrumentation on Java source code coverage”,對JaCoCo的誤差情況以及可能原因進行了詳盡的評估與分析[2]。

JaCoCo與標準基線對比結果顯示:針對不同的專案,在類(Method)層級,JaCoCo的覆蓋率評估就已產生了0.2%-8.5%左右的絕對誤差和0.7%-23.859%的相對誤差。在更細化的語句行和分支層級,其誤差將會被指數級放大。

表格 2 JaCoCo覆蓋率評估的相對誤差[2]

誤差產生的具體成因:

  • 複雜系統通常由大量子模組組成,JaCoCo無法實現對於內部被呼叫的子模組進行插裝,因此對於子模組覆蓋率的評估會產生顯著的誤差。
  • 如果某個子模組沒有被呼叫,那麼對於JaCoCo來說,該模組內的方法等同於不存在。JaCoCo需要呼叫該子模組,才能將該子模組內的程式碼計入覆蓋率計算的“分母”。
  • 除了幾種既定的邏輯意外事件,JaCoCo無法正確處理例外情況(Exception),如果在控制流程中遇到Exception,JaCoCo會把這種情況直接標記為未覆蓋,這種判定方式直接的影響到了對程式邏輯關係的把控,造成對於覆蓋率無法準確評估。

誤差引發的後果:

  • 偽瓶頸的產生,以及對測試質量的錯誤高估。第一種情況,測試人員投入大量工作之後,卻無法進一步提升覆蓋率,造成對資源和實踐的浪費;第二種情況,會讓使用者誤將未達標的系統判定為達標,有可能引發嚴重的生產事故。
  • 無法實現缺陷定位,大量的演算法和應用依託覆蓋率的輸入,而缺陷定位更是其中最主要的實踐。
  • 迴歸測試的精準度,受到了嚴重的影響。

 

五、對接能力分析

JaCoCo對於Ant和Maven有較完整的外掛支援啟動方案,但是隻能和Eclipse或者SonarCube整合,無法實現和測試管理軟體,或是上下游測試工具的完整對接。

對於開發流程,JaCoCo依然是傳統白盒測試的思維,即瀑布式的開發模式,需要在程式碼更新後重新進行測試,每次版本迭代的工作量都十分龐大。

自動化層面,JaCoCo支援與Jenkins的對接。

 

六、結語

JaCoCo能夠有效的在位元組碼層級提供覆蓋率的初步評估和計算,並且有實現從位元組碼關聯到原始碼的能力。但是,JaCoCo只是一款提供粗略評估工具,無法自動關聯用例、無法有效提升測試效率,沒有作為測試中臺的對接和支援能力。JaCoCo的定位和實踐,表明其更適用於偏向輔助個人開發者和小型專案組對專案覆蓋率進行非常基礎的評估,對於支撐大型企業級應用的精準測試需求,依然路途漫長。

 

參考文獻:

[1]      N. Li, X. Meng, J. Offutt, and L. Deng, “Is bytecode instrumentation as good as source code instrumentation: An empirical study with industrial tools (Experience Report),” 2013 IEEE 24th Int. Symp. Softw. Reliab. Eng. ISSRE 2013, pp. 380–389, 2013, doi: 10.1109/ISSRE.2013.6698891.

[2]      D. Tengeri, F. Horváth, Á. Beszédes, T. Gergely, and T. Gyimóthy, “Negative effects of bytecode instrumentation on Java source code coverage,” 2016 IEEE 23rd Int. Conf. Softw. Anal. Evol. Reengineering, SANER 2016, vol. 1, pp. 225–235, 2016, doi: 10.1109/SANER.2016.61.

 

相關文章