3.2-Spring-Boot单元测试MockitoJUnit5全覆盖策略
目录
3.2 Spring Boot单元测试:Mockito+JUnit5全覆盖策略
markdown
# Spring Boot单元测试:Mockito+JUnit5全覆盖策略

## 引言
在持续交付的敏捷开发中,**单元测试覆盖率**是衡量代码质量的核心指标。Spring Boot项目如何通过`JUnit5`+`Mockito`实现**100%测试覆盖率**?本文将手把手教你构建分层测试体系,揭秘单元测试中的**6大避坑指南**,并给出企业级实战方案!
---
## 一、环境搭建与基础配置
### 1.1 依赖引入(Maven)
```xml
<!-- JUnit5核心 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<!-- Mockito框架 -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId> <!-- 支持静态方法Mock -->
<version>4.5.1</version>
<scope>test</scope>
</dependency>
<!-- Spring Boot测试支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
二、分层测试策略
2.1 Service层测试(Mock数据库交互)
java
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
void getUserById_WhenExist_ReturnUser() {
// Given
User mockUser = new User(1L, "test");
when(userRepository.findById(1L)).thenReturn(Optional.of(mockUser));
// When
User result = userService.getUserById(1L);
// Then
assertEquals("test", result.getUsername());
verify(userRepository, times(1)).findById(1L);
}
}
2.2 Controller层测试(MockMvc模拟HTTP)
java
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
void getUser_ShouldReturn200() throws Exception {
when(userService.getUserById(1L))
.thenReturn(new User(1L, "mock_user"));
mockMvc.perform(get("/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.username").value("mock_user"));
}
}
三、Mockito高级技巧
3.1 参数匹配器(ArgumentMatchers)
java
// 模糊匹配任意参数
when(userRepository.save(any(User.class)))
.thenReturn(new User(1L, "saved_user"));
// 自定义参数校验
verify(userService).updateUser(argThat(user ->
user.getUsername().length() > 5));
3.2 异常测试
java
@Test
void transferMoney_WhenBalanceInsufficient_ThrowException() {
when(accountService.getBalance(anyLong()))
.thenReturn(100.0);
assertThrows(InsufficientBalanceException.class, () -> {
transferService.transfer(1L, 2L, 200.0);
});
}
3.3 静态方法Mock(Mockito 3.4+)
java
try (MockedStatic<AuthUtils> utilities = mockStatic(AuthUtils.class)) {
utilities.when(AuthUtils::getCurrentUserId).thenReturn(100L);
assertEquals(100L, userService.getCurrentUserId());
}
四、数据库集成测试
4.1 使用Testcontainers
java
@Testcontainers
@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
class UserRepositoryTest {
@Container
static PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>("postgres:13");
@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
}
@Test
void saveUser_ShouldPersistData() {
User user = repository.save(new User(null, "container_user"));
assertNotNull(user.getId());
}
}
五、覆盖率提升策略
5.1 Jacoco配置(Maven插件)
xml
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.7</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
<goal>report</goal>
</goals>
</execution>
</executions>
<configuration>
<rules>
<rule>
<element>BUNDLE</element>
<limits>
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>0.95</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</plugin>
六、六大避坑指南
过度Mock陷阱
❌错误做法:Mock所有依赖类
✅正确方案:仅Mock外部依赖(数据库、API等),保持业务逻辑真实执行
测试顺序依赖
java
@TestMethodOrder(MethodOrderer.Random.class) // 强制随机执行顺序 class OrderSensitiveTest { ... }
忽略时间敏感测试
java
@Test void scheduleTask_ShouldExecuteWithin5s() { assertTimeoutPreemptively(Duration.ofSeconds(5), () -> { scheduler.executeTask(); }); }
循环依赖导致Mock失效
解决方案 :使用
@MockBean
替代@Autowired
静态方法Mock内存泄漏
java
// 使用try-with-resources确保资源释放 try (MockedStatic<Utility> mocked = mockStatic(Utility.class)) { // ... }
忽略异常分支测试
java
@Test void divide_WhenDivisorIsZero_ThrowException() { assertThrows(ArithmeticException.class, () -> calculator.divide(10, 0)); }
七、企业级最佳实践
测试金字塔模型
单元测试 (70%) > 集成测试 (20%) > 端到端测试 (10%)
测试命名规范
java
// 格式:[Method]_[State]_[Result] @Test void getUserById_WhenIdIsNegative_ThrowIllegalArgument() { ... }
持续集成集成覆盖率检查
yaml
# GitHub Actions示例 - name: Verify coverage run: mvn verify -Djacoco.check=true
结语
通过本文的Mockito+JUnit5组合拳,某金融系统成功将单元测试覆盖率从58%提升至97%,缺陷率下降76%。记住: 不要为了覆盖率而写测试,要为质量而写!
技术拓展 :
👉《Spring Boot集成测试全攻略》
👉《Mockito深度解析》
#SpringBoot# #单元测试# #JUnit5# #Mockito# 更多干货,关注作者获取最新技术动态!