JUnit测试中如何Mock数据库连接,避免真实数据库请求?
解决Spring MVC测试Mock了Service还跑真实数据库请求的问题
先搞懂问题出在哪
- 测试类用了
@SpringBootTest这类加载完整应用上下文的注解,导致Spring初始化了真实的ListsRepository,自然会触发数据库连接 - Controller没有依赖
ListsService,而是直接注入ListsRepository操作数据库,Mock的Service根本没被用到 - Mock的
findById方法参数和实际调用不匹配(比如传int但真实方法需要Long),导致Mock未生效,只能走真实逻辑
一步步解决
1. 用切片测试注解,缩小上下文范围
别用@SpringBootTest,换成@WebMvcTest,它只加载Controller相关的Bean,数据库层完全不会初始化:
@WebMvcTest(ListsController.class) class ListsControllerTest { @Autowired private MockMvc mockMvc; @MockBean private ListsService listsService; // Mock的Service会自动注入到Controller中 @Test void testFindById() throws Exception { // 配置Mock的返回逻辑 when(listsService.findById(1L)).thenReturn(new Lists(1L, "测试列表")); // 模拟接口请求 mockMvc.perform(get("/lists/1")) .andExpect(status().isOk()) .andExpect(jsonPath("$.id").value(1)) .andExpect(jsonPath("$.name").value("测试列表")); } }
2. 确保Controller通过Service操作数据库
检查Controller代码,必须注入ListsService,禁止直接操作ListsRepository:
@RestController @RequestMapping("/lists") public class ListsController { private final ListsService listsService; // 推荐用构造方法注入依赖,测试更清晰 public ListsController(ListsService listsService) { this.listsService = listsService; } @GetMapping("/{id}") public ResponseEntity<Lists> getList(@PathVariable Long id) { Lists list = listsService.findById(id); return ResponseEntity.ok(list); } }
3. 验证Mock方法的参数匹配性
保证Mock的方法参数和Controller调用时完全一致,不确定参数的话用匹配器:
// 匹配任意Long类型参数,避免参数类型/值不匹配导致Mock失效 when(listsService.findById(anyLong())).thenReturn(mockList);
4. 若必须用@SpringBootTest,禁用数据库配置
如果一定要加载完整上下文,就添加注解禁用真实数据库连接,同时Mock底层Repository:
@SpringBootTest @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) // 禁用真实数据库配置 @MockBean(ListsRepository.class) // Mock掉Repository,彻底阻断数据库调用 class ListsControllerFullTest { @Autowired private MockMvc mockMvc; @MockBean private ListsService listsService; // ...测试代码 }
重点提醒
@WebMvcTest是最轻量化的解决方案,完全避免数据库初始化,测试效率更高- 优先用构造方法注入依赖,比字段注入更规范,也更利于测试
- Mock方法时,多用
anyLong()、anyString()这类匹配器,减少参数匹配失败的概率
内容的提问来源于stack exchange,提问作者Peter Penzov
相关产品推荐
相关产品推荐

