Java 单元测试简单扫盲
前言
仔细回想起来,上次认真编写单元测试已经是两年前的事了。那时候觉得写单元测试是种负担。
为了应付代码覆盖率要求,常常依赖工具自动生成测试用例,有时需要启动Spring容器,有时又不需要(当时还分不清单元测试和集成测试的区别)。
直到最近在开发过程中,当需要重构代码或自测功能时,我才真正体会到单元测试的价值。
概念
单元测试的本质是通过独立的测试用例来验证代码单元(方法/函数)的逻辑正确性。
在日常开发中,我们经常会在类里随手写一个 main 方法来快速验证逻辑,比如:
public static void main(String[] args) {
int expectResult = 9;
int result = 3 * 3;
if (result == expectResult) {
System.out.println("测试成功");
} else {
System.out.println("测试失败");
}
}
测试分类
首先介绍一下,单元测试与集成测试的区别,刚开始学习的时候,没有分清它俩的区别,导致一直感觉单元测试是一个很复杂的东西。
集成测试
需要启动完整的 Spring 容器,容器内所有的 Bean 可以正常注入使用。通俗一点,相当于写了一个 TestController。
@SpringBootTest // 启动完整Spring容器
class UserControllerIntegrationTest {
@Autowired // 真实注入Bean
private UserController userController;
@Test
void testGetUser() {
User user = userController.getUser(1L);
assertNotNull(user);
}
}
单元测试
不启动 Spring 容器,所有依赖需要手动模拟
@ExtendWith(MockitoExtension.class) // 启用Mockito注解支持
class UserServiceUnitTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
void testGetUser() {
when(userRepository.findById(1L))
.thenReturn(new User(1L, "Mock用户"));
User user = userService.getUser(1L);
assertEquals("Mock用户", user.getName());
}
}
维度 | 单元测试 | 集成测试 |
---|---|---|
目标 | 验证单个代码单元(如方法、类)的功能正确性 | 验证多个模块、组件或外部系统协同工作的正确性 |
范围 | 隔离测试,不依赖外部系统(如数据库、网络、其他服务) | 依赖外部系统或跨组件的交互(如数据库连接、API 调用) |
覆盖范围 | 聚焦分支,验证代码片段逻辑正确性 | 聚焦模块间交互,验证系统整体行为 |
核心注解介绍
@Resource (Spring标准注解)
作用:用于依赖注入,会按照名称或类型从Spring容器中获取真实的bean
测试场景:集成测试中需要完全使用真实逻辑时
@MockBean (Spring Boot测试注解)
作用:向Spring应用上下文注入一个mock对象,替代原有的bean
测试场景:Spring Boot集成测试中需要mock某些bean时
@Spy (Mockito注解)
作用:创建部分mock对象,未mock的方法使用真实逻辑,mock的方法使用自定义逻辑
测试场景:需要保留对象大部分真实行为,仅修改少数方法逻辑的测试
@Mock (Mockito注解)
作用:创建完整mock对象,所有方法都需要mock,未mock的方法会返回默认值或空集合
测试场景:需要完全模拟依赖行为的单元测试
@InjectMocks (Mockito注解)
作用:自动将@Mock或@Spy创建的mock对象注入到被测试对象中
测试场景:需要自动装配依赖的单元测试
使用
依赖文件
添加依赖,具体版本需要根据项目版本自己替换
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
单元测试
@Spy
与 @Mock
的区别:
@Mock
所有方法都需要 mock,未 mock 的方法会返回默认值或空集合 ; @Spy
未 mock 的方法使用真实逻辑,mock 的方法使用自定义逻辑
注意:没有无参构造会初始化为空指针,需要手动实例化进行解决
@Spy
private UserService userService = new UserService(); // 显式提供实例
集成测试
@MockBean:向Spring上下文注入 Mock对象
@SpringBootTest
public class UserServiceTest {
@Autowired
private UserService userService; // 注入真实 UserService
@MockBean
private UserRepository userRepository; // 替换 Spring 容器中的 UserRepository
@Test
public void testGetUser() {
User mockUser = new User(1, "Alice");
when(userRepository.findById(1)).thenReturn(Optional.of(mockUser));
User result = userService.getUser(1);
assertEquals("Alice", result.getName());
verify(userRepository).findById(1);
}
}
总结
更多的内容还需要在探索后补充
本文来自博客园,作者:帅气的涛啊,转载请注明原文链接:https://www.cnblogs.com/handsometaoa/p/18835301