Jacoco 的 Maven 多模塊項目覆蓋
1. 概述
在本教程中,我們將構建一個 Maven 多模塊項目。在這個項目中,服務和控制器將位於不同的模塊中。然後,我們將編寫一些測試並使用 Jacoco 來計算代碼覆蓋率。
2、服務層
首先,讓我們創建多模塊應用程序的服務層。
2.1.服務等級
我們將創建我們的服務並添加一些方法:
@Service
class MyService {
String unitTestedOnly() {
return "unit tested only";
}
String coveredByUnitAndIntegrationTests() {
return "covered by unit and integration tests";
}
String coveredByIntegrationTest() {
return "covered by integration test";
}
String notTested() {
return "not tested";
}
}
正如他們的名字所示:
- 位於同一層的單元測試將測試方法
unitTestedOnly()
- 單元測試將測試
coveredByUnitAndIntegrationTests()
。控制器模塊中的集成測試也將涵蓋此方法的代碼 - 集成測試將涵蓋
coveredByIntegrationTest()
。但是,沒有單元測試會測試此方法 - 沒有測試將覆蓋方法
notTested()
2.2.單元測試
現在讓我們編寫相應的單元測試:
class MyServiceUnitTest {
MyService myService = new MyService();
@Test
void whenUnitTestedOnly_thenCorrectText() {
assertEquals("unit tested only", myService.unitTestedOnly());
}
@Test
void whenTestedMethod_thenCorrectText() {
assertEquals("covered by unit and integration tests", myService.coveredByUnitAndIntegrationTests());
}
}
測試只是檢查方法的輸出是否符合預期。
2.3. Surefire 插件配置
我們將使用 Maven Surefire 插件來運行單元測試。讓我們在服務的模塊pom.xml
中配置它:
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.1.2</version>
<configuration>
<includes>
<include>**/*Test.java</include>
</includes>
</configuration>
</plugin>
</plugins>
3. 控制器層
我們現在將在多模塊應用程序中添加控制器層。
3.1.控制器類
讓我們添加控制器類:
@RestController
class MyController {
private final MyService myService;
public MyController(MyService myService) {
this.myService = myService;
}
@GetMapping("/tested")
String fullyTested() {
return myService.coveredByUnitAndIntegrationTests();
}
@GetMapping("/indirecttest")
String indirectlyTestingServiceMethod() {
return myService.coveredByIntegrationTest();
}
@GetMapping("/nottested")
String notTested() {
return myService.notTested();
}
}
fullyTested()
和indirectlyTestingServiceMethod()
方法將通過集成測試進行測試。因此,這些測試將涵蓋兩個服務方法coveredByUnitAndIntegrationTests()
和coveredByIntegrationTest()
。另一方面,我們不會為notTested()
編寫測試。
3.2.集成測試
我們現在可以測試我們的RestController
:
@SpringBootTest(classes = MyApplication.class)
@AutoConfigureMockMvc
class MyControllerIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Test
void whenFullyTested_ThenCorrectText() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/tested"))
.andExpect(MockMvcResultMatchers.status()
.isOk())
.andExpect(MockMvcResultMatchers.content()
.string("covered by unit and integration tests"));
}
@Test
void whenIndirectlyTestingServiceMethod_ThenCorrectText() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/indirecttest"))
.andExpect(MockMvcResultMatchers.status()
.isOk())
.andExpect(MockMvcResultMatchers.content()
.string("covered by integration test"));
}
}
在這些測試中,我們啟動應用程序服務器並向其發送請求。然後,我們檢查輸出是否正確。
3.3.故障安全插件配置
我們將使用 Maven Failsafe 插件來運行集成測試。最後一步是在控制器的模塊pom.xml
中配置它:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.1.2</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
<configuration>
<includes>
<include>**/*IntegrationTest.java</include>
</includes>
</configuration>
</plugin>
4. 通過 Jacoco 聚合覆蓋範圍
Jacoco(Java代碼覆蓋率)是Java應用程序中用於測量測試期間代碼覆蓋率的工具。現在讓我們計算我們的覆蓋率報告。
4.1.準備Jacoco代理
prepare-agent
階段設置必要的掛鉤和配置,以便 Jacoco 可以在運行測試時跟踪執行的代碼。在運行任何測試之前需要進行此配置。因此,我們將準備步驟直接添加到父pom.xml
中:
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.8</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
</executions>
</plugin>
4.2.收集測試結果
為了收集測試覆蓋率,我們將創建一個新模塊aggregate-report
。它只包含一個pom.xml
,並且依賴於前面的兩個模塊。
由於準備階段,我們可以匯總每個模塊的報告。這是report-aggregate
目標的工作:
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.8</version>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>report-aggregate</goal>
</goals>
<configuration>
<dataFileIncludes>
<dataFileInclude>**/jacoco.exec</dataFileInclude>
</dataFileIncludes>
<outputDirectory>${project.reporting.outputDirectory}/jacoco-aggregate</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
我們現在可以從父模塊運行verify
目標:
$ mvn clean verify
在構建結束時,我們可以看到Jacoco在aggregate-report
子模塊的target/site/jacoco-aggregate
文件夾中生成了報告。
讓我們打開index.html
文件來看看結果。
首先,我們可以導航到控制器類的報告:
正如預期的那樣,測試涵蓋了構造函數、 fullyTested()
和indirectlyTestingServiceMethod()
方法,而notTested()
則沒有涵蓋。
現在讓我們看一下服務類別的報告:
這次,讓我們重點關注coveredByIntegrationTest()
方法。據我們所知,服務模塊中沒有測試測試此方法。通過此方法代碼的唯一測試是在控制器模塊內部。然而,Jacoco 認識到這種方法是有測試的。在這種情況下,“聚合”一詞就具有其全部含義!
5. 結論
在本文中,我們創建了一個多模塊項目並通過 Jacoco 收集了測試覆蓋率。
讓我們回想一下,我們需要在測試之前運行準備階段,而聚合則在測試之後進行。為了更進一步,我們可以使用像 SonarQube 這樣的工具來獲得覆蓋結果的良好概述。
與往常一樣,代碼可以在 GitHub 上獲取。