如果 JUnit 覆蓋率低於特定閾值,則 Maven 構建失敗
1. 概述
在本教程中,我們將了解當 JaCoCo 代碼覆蓋率低於特定閾值時如何導致 Maven 構建失敗。我們將從沒有門檻的普通 JaCoCo 插件開始。然後我們將向現有的 JaCoCo 插件添加一個新的執行,重點檢查覆蓋範圍。
在這裡,我們將討論這個新執行的一些顯著元素。然後,我們將擴展ProductService
的一個簡單示例,以查看BRANCH
和INSTRUCTION
覆蓋範圍規則的效果。我們將看到構建因特定規則而失敗。最後,我們將總結與 JaCoCo 一起執行質量控制規則的潛在好處。
2.JaCoCo Maven 插件
讓我們首先使用簡單形式的 JaCoCo 插件。這意味著當我們執行mvn clean install
時,我們將使用它來計算代碼覆蓋率並生成報告:
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco.version}</version>
<configuration>
<excludes>
<exclude>com/baeldung/**/ExcludedPOJO.class</exclude>
<exclude>com/baeldung/**/*DTO.*</exclude>
<exclude>**/config/*</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>jacoco-initialize</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>jacoco-site</id>
<phase>package</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
3. 設置示例
接下來,我們以兩個簡單的服務為例,即CustomerService
和ProductService
。讓我們在ProductService
中添加一個getSalePrice()
方法,該方法有兩個分支,一個用於標誌設置為true
的情況,另一個用於標誌設置為false:
public double getSalePrice(double originalPrice, boolean flag) {
double discount;
if (flag) {
discount = originalPrice - originalPrice * DISCOUNT;
} else {
discount = originalPrice;
}
return discount;
}
讓我們編寫兩個單獨的測試來覆蓋boolean
true
和false
的兩個條件:
@Test
public void givenOriginalPrice_whenGetSalePriceWithFlagTrue_thenReturnsDiscountedPrice() {
ProductService productService = new ProductService();
double salePrice = productService.getSalePrice(100, true);
assertEquals(salePrice, 75);
}
@Test
public void givenOriginalPrice_whenGetSalePriceWithFlagFalse_thenReturnsDiscountedPrice() {
ProductService productService = new ProductService();
double salePrice = productService.getSalePrice(100, false);
assertEquals(salePrice, 100);
}
類似地, CustomerService
包含一個簡單的方法getCustomerName()
:
public String getCustomerName() {
return "some name";
}
接下來,這是相應的單元測試,以顯示報告中此方法的覆蓋率:
@Test
public void givenCustomer_whenGetCustomer_thenReturnNewCustomer() {
CustomerService customerService = new CustomerService();
assertNotNull(customerService.getCustomerName());
}
4. 使用 JaCoCo 設置測試覆蓋率基線
有了一組基本的類和測試設置,我們首先進行mvn clean install
並觀察 JaCoCo 報告的結果。這有助於我們設置當前模塊測試覆蓋率的基線:
至此,模塊構建成功。接下來我們看一下JaCoCo的報告,看看目前的報導情況:
從報告中我們可以看到,目前的覆蓋率是 72%,我們已經成功構建了 Maven。
5. 在 JaCoCo 插件中添加規則
現在,我們將規則應用於插件,以便基於指令數量的總體覆蓋率不低於 70%,並且分支覆蓋率不應低於 68%。
為此,我們在插件中定義一個新的執行:
<execution>
<id>check-coverage</id>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
<configuration>
<rules>
<rule>
<element>BUNDLE</element>
<limits>
<limit>
<counter>INSTRUCTION</counter>
<value>COVEREDRATIO</value>
<minimum>0.70</minimum>
</limit>
<limit>
<counter>BRANCH</counter>
<value>COVEREDRATIO</value>
<minimum>0.68</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
在這裡,我們在 JaCoCo 插件中使用 id check-coverage
定義了一個新的執行。我們已將其與 Maven 構建的verify
階段相關聯。與此執行相關的目標設置為check
,這意味著此時應執行jacoco:check
。接下來,我們定義了 j acoco:check.
接下來,讓我們仔細研究<rule>
標記中定義的組件:
5.1. JaCoCo 中的<element>
規則的第一個重要部分是<element>
標記,它定義應用規則的覆蓋元素。覆蓋元素代表代碼中不同的粒度級別,例如類、包、方法或行。我們可以通過指定適當的覆蓋元素來為該特定級別設置特定的覆蓋標準。
在這裡,我們在BUNDLE
級別設置了覆蓋率閾值,它代表了正在分析的整個代碼庫的總體覆蓋率。它將來自不同單元(例如類、包或模塊)的覆蓋率信息組合成單個聚合結果。它通常用於定義最高級別的覆蓋率規則或閾值,給出整個項目的代碼覆蓋率的概述
此外,如果我們想在類級別設置覆蓋閾值,我們將使用<element> CLASS </element>
。
同樣,如果我們想在行級別設置閾值,我們可以使用<element>
LINE
</element>
。
5.2. JaCoCo 中的<limits>
一旦我們在這裡定義了粒度( BUNDLE
),我們就定義一個或多個限制。本質上, limit
封裝了有關我們希望如何使用一個或多個計數器強制執行代碼覆蓋率的信息。
每個limit
包含相應的counter
、 value
和<minimum>
或<maximum>
標籤。 <value>
標記的可能選項包括COVEREDRATIO
、 COVEREDCOUNT
、 MISSEDCOUNT
和TOTALCOUNT
。在我們的示例中,我們使用COVEREDRATIO
表示覆蓋率。
我們將在下一節中更詳細地討論計數器。
5.3. JaCoCo 中的<counter>
本質上,這裡我們希望基於兩個計數器對BUNDLE
強制執行閾值; BRANCH
計數器和INSTRUCTION
計數器。對於這兩個計數器,我們使用COVEREDRATIO
作為閾值的度量。對於此示例,我們希望INSTRUCTION
覆蓋率至少為 72%, BRANCH
覆蓋率至少為 68%。
INSTRUCTION
計數器對於了解代碼執行級別和識別測試可能遺漏的區域非常有價值。 BRANCH
計數器能夠識別決策點覆蓋範圍,確保它執行true
和false
分支。
LINE
計數器可以洞察代碼覆蓋的粒度,使我們能夠確定覆蓋了哪些單獨的代碼行。
計數器的其他可能選項包括LINE
、 COMPLEXITY
、 METHOD,
和CLASS
。這些計數器提供了有關代碼覆蓋率的不同視角,可用於分析代碼庫的不同方面。
選擇哪些計數器更有用取決於項目的具體目標和要求。然而, INSTRUCTION
、 BRANCH
和LINE
計數器通常是最常用的,可以很好地全面了解代碼覆蓋率。
6. 覆蓋率降低導致構建失敗
要查看我們的構建因新實施的規則而失敗,讓我們禁用以下測試:
@Test
@Disabled
public void givenOriginalPrice_whenGetSalePriceWithFlagFalse_thenReturnsDiscountedPrice() {//...}
現在,讓我們再次運行命令mvn clean install
:
我們發現此時構建失敗,參考失敗的覆蓋率檢查:
[ERROR] Failed to execute goal org.jacoco:jacoco-maven-plugin:0.8.6:check (check-coverage) on project testing-libraries-2: Coverage checks have not been met.
重要的是,我們觀察到jacoco: check
目標執行了。它使用Jacoco.exec
作為分析源。接下來,它按照我們在規則的元素標記中指定的那樣對BUNDLE
級別執行分析,並發現 Maven 構建對於INSTRUCTION
和BRANCH
計數器均失敗。
如結果所示, COVEREDRATIO
的INSTRUCTION
計數器值降至 0.68 或 68%,而最小值為 0.70 或 70%。此外, BRANCH
計數器值為 0.50 或 50%,而預期最小值為 68%。
為了修復構建,我們需要恢復BUNDLE
元素規定的整體覆蓋範圍,使指令INSTRUCTION
>= 70%, BRANCH
覆蓋率 >=68%。
七、結論
在本文中,我們了解瞭如果代碼覆蓋率低於特定閾值,如何使 Maven 構建失敗。我們研究了 JaCoCo Maven 插件提供的各種選項,以在不同粒度級別強制執行閾值。該示例重點關注值為COVEREDRATIO
的BRANCH
和INSTRUCTION
計數器。
執行覆蓋率規則可確保我們的代碼庫始終滿足最低水平的測試覆蓋率。總而言之,這有助於通過識別缺乏足夠測試的領域來提高軟件的整體質量。通過在不滿足覆蓋率規則時使構建失敗,我們可以防止覆蓋率不足的代碼在開發生命週期中進一步發展,從而降低發布未經測試或低質量代碼的可能性
與往常一樣,源代碼可以在 GitHub 上獲取。