jacoco初体验
背景
jacoco是一个输出单元测试覆盖率的工具。
初体验
依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.7.1</version>
<scope>test</scope>
</dependency>
<!-- junit 5 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.7.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<!-- jacoco: generate test coverage report -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.8</version>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
类文件
package com.abeffect.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MainDemoApp {
public static void main(String[] args) {
SpringApplication.run(MainDemoApp.class, args);
}
}
Controller
package com.abeffect.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(path = "/hello")
public class HelloController {
@GetMapping(path = "/{branchId}")
public String getBranch(@PathVariable("branchId") int branchId) {
if (branchId == 0) {
return "master";
} else if (branchId == 1) {
return "dev";
}
return "release";
}
}
Test
package com.abeffect.demo;
import com.abeffect.demo.controller.HelloController;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class HelloControllerTest {
static MockMvc mockMvc;
@BeforeAll
static void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(new HelloController()).build();
}
@Test
void testGetBranch() {
Map<Integer, String> testCases = new HashMap<>();
testCases.put(0, "master");
testCases.put(1, "dev");
testCases.put(3, "release");
testCases.forEach((key, value) -> {
try {
mockMvc
.perform(MockMvcRequestBuilders.get("/hello/{branchId}", key)
.accept(MediaType.APPLICATION_JSON)
.characterEncoding(StandardCharsets.UTF_8.name()))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().string(value))
.andReturn();
} catch (Exception e) {
e.printStackTrace();
}
});
}
}
运行
mvn clean test
效果
多模块版本
如果对一个使用了多module的项目,还使用上面的方式,会导致覆盖的类不全。
因此,需要按如下设置。
修改root pom
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.8</version>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
修改gw pom
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<argLine>${argLine} -Xms256m -Xmx2048m</argLine>
<forkCount>1</forkCount>
<runOrder>random</runOrder>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.8</version>
<executions>
<execution>
<id>report-aggregate</id>
<phase>test</phase>
<goals>
<goal>report-aggregate</goal>
</goals>
</execution>
</executions>
</plugin>
原理
为什么要用 mvn verity
而不是 mvn test
原因:
- process-classes 打桩:jacoco 的 instrument 会在 maven 生命周期的 process-classes 阶段编译好的代码打桩用于统计代码覆盖率
- restore-instrumented-classes 恢复打桩
maven完整的生命周期
validate
initialize
generate-sources
process-sources
generate-resources
process-resources
compile
process-classes ............. (instrument 默认所属周期)
generate-test-sources
process-test-sources
generate-test-resources
process-test-resources
test-compile
process-test-classes
test ........................ (mvn test 执行的截止周期)
prepare-package ............. (restore-instrumented-classes 默认所属周期)
package
pre-integration-test
integration-test
post-integration-test
verify
install