如何为Spring Boot的/getSUID/{pid}接口编写JUnit与Mockito测试用例
针对该接口的JUnit + MockMvc测试实现方案
核心思路是完全不启动真实Spring容器、不连接真实数据库,通过Mockito隔离所有外部依赖,避开之前全量容器加载带来的各类报错。
测试整体依赖梳理
这个接口运行时的外部依赖共有3类,测试时需要全部替换为Mock对象,不能执行真实逻辑:
JdbcTemplate:数据库查询组件,真实调用会连数据库引发超时、连接失败错误QueriesUtil:SQL加载工具类,是静态方法调用,真实执行会加载本地SQL配置文件,容易报文件找不到、空指针错误- 数据库返回结果的RowMapper映射逻辑
测试类基础配置
采用Mockito的纯单元测试模式,搭配standalone模式的MockMvc,相比启动全量SpringBootTest的方式,启动速度快、报错点少:
@ExtendWith(MockitoExtension.class) class StudyControllerTest { // 注入待测试的Controller实例,自动注入同类型的Mock依赖 @InjectMocks private StudyController studyController; // 声明需要Mock的数据库组件 @Mock private JdbcTemplate jdbcTemplate; private MockMvc mockMvc; // 每个测试方法执行前初始化MockMvc @BeforeEach void setUp() { // 仅注册当前测试的Controller,不加载其他Spring容器组件 mockMvc = MockMvcBuilders.standaloneSetup(studyController).build(); } }
注意:很多测试报错来自使用
@AutoConfigureMockMvc搭配全量@SpringBootTest启动,加载了不需要的数据库、配置类Bean导致上下文启动失败。用standaloneSetup初始化的MockMvc完全不依赖容器,稳定性更高。
核心测试方法编写(重点解决静态方法Mock问题)
针对QueriesUtil.getQuery()这类静态方法,必须用Mockito的静态Mock能力做隔离,否则测试时会执行真实的SQL加载逻辑,这也是很多人编写测试时频繁报错的核心原因:
@Test void testGetSUID_NormalRequest() throws Exception { // 用try-with-resources隔离静态Mock的作用域,避免Mock值残留影响其他测试用例 try (MockedStatic<QueriesUtil> queriesUtilMock = Mockito.mockStatic(QueriesUtil.class)) { // 1. Mock SQL加载逻辑的返回值 String mockBaseSql = "SELECT PERSON_ID, MODALITY, STUDY_UID FROM STUDY WHERE PID = <:PID>"; queriesUtilMock.when(() -> QueriesUtil.getQuery("STUDY_UID_TEST", true)) .thenReturn(mockBaseSql); // 2. 构造测试入参 String testPid = "P00123"; // 构造模拟的数据库返回对象 StudyUID mockStudyResult = new StudyUID( 1001, "MR", "1.2.156.10019.2.123456" ); // 3. Mock JdbcTemplate的查询返回值,不执行真实数据库查询 when(jdbcTemplate.query(anyString(), any(RowMapper.class))) .thenReturn(List.of(mockStudyResult)); // 4. 发起MockMvc请求 mockMvc.perform(get("/getSUID/{pid}", testPid) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) // 校验接口返回的JSON结构和值是否符合预期 .andExpect(jsonPath("$[0].personId").value(1001)) .andExpect(jsonPath("$[0].modality").value("MR")) .andExpect(jsonPath("$[0].studyUid").value("1.2.156.10019.2.123456")); // 5. 校验业务逻辑的交互正确性 // 确认SQL加载时用了正确的配置key queriesUtilMock.verify(() -> QueriesUtil.getQuery("STUDY_UID_TEST", true), times(1)); // 确认SQL占位符替换逻辑生效:传入的pid正确替换了SQL里的<:PID>占位符 String expectedReplacedSql = "SELECT PERSON_ID, MODALITY, STUDY_UID FROM STUDY WHERE PID = P00123"; verify(jdbcTemplate, times(1)).query(eq(expectedReplacedSql), any(RowMapper.class)); } }
常见报错排查点
- 静态Mock没有用try-with-resources做作用域隔离,导致静态Mock值残留到其他测试用例,引发逻辑异常
- 匹配JdbcTemplate的RowMapper参数时用了固定值匹配,因为接口里的RowMapper是lambda匿名实现,固定值匹配会识别失败,导致Mock不生效、返回null
- 请求路径的占位符传递错误,要么路径写死和接口映射不匹配,要么动态参数没有传递给
get()方法,导致404错误 - 静态Mock的依赖版本不兼容:需要引入mockito-inline组件才能支持静态方法Mock,普通版本的Mockito默认不支持该能力
内容的提问来源于stack exchange,提问作者Neha Pal
相关产品推荐
相关产品推荐

