项目开发中可能会遇到:单元测试代码覆盖率达到 XX% 的要求。因为 spring boot 提供了 test starter , 所以很多人就直接基于 @SpringBootTest 做起了所谓的单元测试。
这样做存在一些问题:
- 启动慢
- 依赖数据库,且对数据库有影响
基于 @SpringBootTest 的测试,其实是集成测试,自然也有其存在的价值的,但不应该用来做单元测试。
做单元测试,通常需要三个其础工具
- 测试框架 (Jnuit4 )
- Mock 工具(Mockit)
- 断言
而这些已经被 test starter 所依赖了,所以开发者不必再操心引入了。
package com.yjh.study.spring.boot.demo.service;
import com.yjh.study.spring.boot.demo.dao.UnitTestDao;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.*;
import org.springframework.test.util.ReflectionTestUtils;
class UnitTestServiceTest {
// 要被测试的类,@InjectMocks 表示将被 @Mock 标记的对象注入到当前对象中
@InjectMocks
private UnitTestService unitTestService;
// 通过动态代理生成一个假对象
@Mock
private UnitTestDao unitTestDao;
// 这里想测试 mock 一个 final 类,因为使用的是 spring boot 3 成功了,早一些的板本会失败
@Mock
private FinalClassService finalClassService;
@BeforeEach
void setUp() {
// 使 @Mock @InjectMocks 生效;更早的板本中是通过调用 initMocks 方法
MockitoAnnotations.openMocks(this);
}
@AfterEach
void tearDown() {
}
@Test
void findById() {
// 打桩
String param = "123";
Mockito.doReturn(param).when(unitTestDao).findById(param);
// 调用要测试的方法,并断言
Assertions.assertEquals(param, unitTestService.findById(param));
// 使用 spring 提供的工具,测试 private 方法
String findResult1 = ReflectionTestUtils.invokeMethod(unitTestService, "find", param);
Assertions.assertEquals(param, findResult1);
// 对于没有打桩过的参数,方法返回 null
String findResult2 = ReflectionTestUtils.invokeMethod(unitTestService, "find", "111");
Assertions.assertNull(findResult2);
// 静态方法的 mock
try (MockedStatic<StaticMethodService> mockStatic = Mockito.mockStatic(StaticMethodService.class)) {
mockStatic.when(() -> StaticMethodService.generate(param))
.thenReturn(param + param);
Assertions.assertEquals(param + param, StaticMethodService.generate(param));
}
}
}
上边代码使用的 spring boot 板本 3.1.3。在 spring boot 2 中可能存在 final class 不能被 mock 的情况。解决方法:
创建文件:
src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
文件中写入
mock-maker-inline
更多细节问题可以查 Junit 和 Mockito 的官方文档。