覆蓋率計數器
JaCoCo使用一組不同的計數器來計算覆蓋率指標。所有這些計數器都是從Java類文件里獲取信息,這些類文件包含Java 字節碼指令和調試信息。即使沒有可用源代碼情況下,這種方法可以實時有效的對應用程序進行檢測和分析。在大多數情況下,收集的信息可以映射回源代碼,並可視化到每一行代碼的粒度。然而,這種方法也有局限性。這些類文件必須使用調試信息編譯,這樣才可以計算行的覆蓋率並讓源碼高亮顯示。並不是所有的Java語言的結構都可以直接編譯成相應的字節碼。在這種情況下,Java編譯器創建所謂的“合成”代碼,有時會導致未預料到的代碼覆蓋結果。
指令(C0 覆蓋率)
JaCoCo最小的計數單元是單個Java字節代碼指令。指令覆蓋率提供了代碼是否被執行的信息。這個度量是完全獨立於源格式並且經常可用,即使在類文件缺失調試信息的情況下。
分支(C1 覆蓋率)
JaCoCo還計算所有的 if 和 switch 語句的分支覆蓋率。這個度量計算一個方法里的總分支數並確定執行或未執行的分支數。分支覆蓋率總是可用的,即使類文件里缺失調試信息的情況下。請注意異常處理是不在分支度量里面統計的。
如果類文件使用調試信息編譯,產生的覆蓋率可以映射到源碼行並且高亮顯示:
- 沒有覆蓋:行內沒有分支被執行(紅色代碼塊)
- 部分覆蓋:行內只有一部分分支被執行(黃色代碼塊)
- 完全覆蓋:行內所有分支都被執行 (綠色代碼塊)
圈復雜度
JaCoCo同樣可以為每一個非抽象方法計算圈復雜度,最終計算出類、包、組的復雜度。根據McCabe1996圈復雜度的定義是,在線性組合中,計算在一個方法里面所有可能路徑的最小數量。因此,復雜度的值可以作為表示單元測試用例是否有完全覆蓋所有場景的一個依據。復雜度數字即使在類文件缺失調試信息的情況下也可以計算。
圈復雜度 v(G) 的正式定義基於方法的控制流程圖作為有向圖的表示:
v(G) = E - N + 2
其中,E代表邊界的數量,N表示節點的數量。JaCoCo通過下面基於分支數量(B)和決策點數量(D)的等價方程來計算方法的圈復雜度:
v(G) = B - D + 1
基於每個分支的被覆蓋情況,JaCoCo也為每個方法計算覆蓋率和缺失的復雜度。缺失的復雜度同樣表示測試用例沒有完全覆蓋到這個模塊。值得注意的是JaCoCo並不會將異常處理作為分支, try/catch 塊也同樣不會增加復雜度。
行
所有的類文件使用調試信息編譯之后,就可以計算行的覆蓋率信息。一行源代碼是否被執行,要看這一行中是否至少有一個指令被執行。由於單一行代碼通常被編譯為多個字節碼指令,這樣源碼在高亮顯示時,會顯示成3種不同的狀態:
- 沒有覆蓋:行中沒有執行任何指令 (紅色背景)
- 部分覆蓋:行中只有一部分指令被執行(黃色背景)
- 完全覆蓋:行中的所有指令都已執行(綠色背景)
根據源代碼格式的不同,源代碼的單行可能引用多個方法或多個類。因此,不能簡單地添加方法的行計數來獲取包含類的總數。同一個源文件中多個類的行也是如此。Jacoco根據覆蓋的實際源行計算類和源文件的行覆蓋率。
方法
每個非抽象方法至少包含一個指令。一個方法是否執行取決於方法中是否至少有一個指令被執行。當Jacoco在字節代碼級別上工作時,構造器和靜態初始化同樣會像方法一樣統計。其中一些方法可能沒有可以直接對應的Java源碼,比如默認構造器或常量的初始化命令。
類
一個類是否執行取決於類中是否至少有一個方法被執行。值得注意的是JaCoCo認為構造函數和靜態初始化都是方法。Java的接口一般包含靜態初始化,所以接口也同樣被認為是可執行的類。