本文最后更新于:2022年11月9日 下午
没人能够保证自己的代码万无一失,除非其代码执行覆盖率达到100%
我们在项目开发中,期望能够对接口和代码进行单元测试,并且要求代码覆盖率要高于80%。这样做的目的是为了后续交接代码之后,其他人修改了代码,可能会影响到其他的系统,我们能够在代码执行单元测试的时候将问题暴露出来。那么我们期望开发人员在本地跑单元测试的时候,能够看到自己代码的覆盖率是多少,并且展示具体是哪里的代码覆盖到了,哪些没有覆盖。本文就是介绍使用jacoco插件来生成单元测试覆盖率报告,并且查看覆盖明细。
1.创建Maven工程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId> <artifactId>JacocoDemo</artifactId> <version>1.0-SNAPSHOT</version>
<properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <jacoco.version>0.8.6</jacoco.version> </properties>
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.4.1</version> </parent>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-params</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <scope>test</scope> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>${jacoco.version}</version> <executions> <execution> <goals> <goal>prepare-agent</goal> </goals> </execution> <execution> <id>generate-code-coverage-report</id> <phase>test</phase> <goals> <goal>report</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
|
我们使用了Junit5,并创建一个web工程进行测试,我在pom中进行了备注。
2.创建SpringBoot启动类
org.example.jacocodemo.App
:
1 2 3 4 5 6 7 8 9 10 11 12
| package org.example.jacocodemo;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } }
|
3.创建Controller:
DemoController
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package org.example.jacocodemo;
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 = "/test") public class DemoController {
@GetMapping(path = "/{branchId}") public String getBranch(@PathVariable("branchId") int branchId) { if (branchId == 0) { return "master"; } else if (branchId == 1) { return "dev"; } return "release"; } }
|
我们这里的思路非常简单,我们通过传递过来的branchId来做代码分支的划分,通过设计不同的测试用例,来实现不同的代码覆盖率。
4.创建单元测试
DemoControllerTest
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| package org.example.jacocodemo;
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 DemoControllerTest { static MockMvc mockMvc;
@BeforeAll static void setUp() { mockMvc = MockMvcBuilders.standaloneSetup(new DemoController()).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("/test/{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(); } }); } }
|
这里使用了junit5的语法,使用SpringBootTest和ExtendWith注解来启动单元测试,指定了使用随机端口,并通过MockMvc来模拟Controller调用。MockMvc的初始化工作在setUp中完成,用Map来模拟测试用例和期望的结果。
5.运行单元测试
在工程根目录下执行mvn clean test
来执行单元测试并生成覆盖率报告,生成后的报告为./target/site/jacoco/index.html
,通过浏览器打开该文件,可以看到整体的覆盖率以及单元测试的情况: 按照层级点击到我们的DemoController可以看到代码的覆盖率和分支覆盖率,点击进入文件,选择方法可以看到执行了方法中的哪些行代码:
PS:由于SpringBoot默认使用的是junit4,在junit4的情况下我执行mvn clean test
的时候,mvn-surfire-maven-plugin不执行我的单元测试,运行的测试那里一直显示0。查询了一些解决方法,没有生效,最后改为使用junit5,各位也可以根据自己项目中使用的技术酌情处理和修改。如果有解决junit4下单元测试不执行的方法,还请评论留言,供大家学习。
References