Kotlin+Mockk环境下如何Mock SpringBoot Repository避免测试操作真实数据库
问题原因
你在测试类中手动构造的standingOrderRepository、standingOrderServiceImpl等Mock实例,没有被纳入Spring的测试上下文管理。Spring容器运行时仍然使用自动扫描创建的真实实现类,所以不管是MockMvc发起的请求,还是直接调用上下文内的Bean方法,都会触发真实数据库的读写操作。
解决方案
分为两种常用测试场景适配:
场景1:保留@SpringBootTest集成测试,使用MockMvc测试接口
这是你当前的测试写法,只需要替换Mock的注入方式即可:
- 首先引入SpringMockk依赖,实现Mockk和Spring Boot测试体系的适配
Gradle依赖:
testImplementation "com.ninja-squad:springmockk:4.0.2"
Maven依赖:
<dependency> <groupId>com.ninja-squad</groupId> <artifactId>springmockk</artifactId> <version>4.0.2</version> <scope>test</scope> </dependency>
- 修改测试类代码,用
@MockkBean注解替换手动Mock的Bean,Spring会自动将上下文内的对应Bean替换为Mock实例,不需要你手动构造Controller
修改后的测试类示例:
@SpringBootTest @AutoConfigureMockMvc internal class StandingOrderResourceTest { @Autowired lateinit var mockMvc: MockMvc @Autowired lateinit var objectMapper: ObjectMapper // 用@MockkBean替换手动mock,自动注入到Spring上下文 @MockkBean lateinit var standingOrderMapper: StandingOrderMapper @MockkBean lateinit var standingOrderRepository: StandingOrderRepository @MockkBean lateinit var standingOrderServiceImpl: StandingOrderServiceImpl val baseUrl = "/api" @Nested @DisplayName("GetStandingOrders()") @TestInstance(TestInstance.Lifecycle.PER_CLASS) inner class GetStandingOrders { @Test fun `should return all StandingOrders`() { // 先给Mock方法定义Stub返回规则 val testStandingOrders = listOf(StandingOrder()) // 构造你的测试实体 val testDtoList = listOf(StandingOrderDto()) // 构造你的测试返回DTO every { standingOrderRepository.findAll() } returns testStandingOrders every { standingOrderServiceImpl.getStandingOrders() } returns testDtoList mockMvc.get(baseUrl) .andDo { print() } .andExpect { status { isOk() } content { contentType(MediaType.APPLICATION_JSON)} // 可以补充返回内容校验 jsonPath("$[0].id") { value(testDtoList[0].id) } } } } }
场景2:纯单元测试,不需要启动Spring容器(执行速度更快)
如果只需要测试Controller的逻辑,不需要测试Spring框架相关的拦截器、参数绑定等能力,可以直接走纯单元测试:
internal class StandingOrderResourceTest { private val standingOrderMapper = mockk<StandingOrderMapper>() private val standingOrderRepository = mockk<StandingOrderRepository>() private val standingOrderServiceImpl = mockk<StandingOrderServiceImpl>() // 手动构造Controller,注入Mock的Service private val standingOrderResource = StandingOrderResource(standingOrderServiceImpl) @Test fun `should return all StandingOrders`() { val testDtoList = listOf(StandingOrderDto()) every { standingOrderServiceImpl.getStandingOrders() } returns testDtoList val result = standingOrderResource.getStandingOrders() assertEquals(testDtoList, result) } }
注意事项
- 不要混用手动构造实例和Spring自动注入实例,两种测试模式二选一:要么完全不启动Spring容器手动构造所有依赖做单元测试,要么全交给Spring上下文管理,用
@MockkBean替换需要Mock的Bean - 所有Mock对象的被调用方法必须提前定义Stub规则,否则Mockk会抛出
MissingStubInvocationException异常
内容的提问来源于stack exchange,提问作者X5H4Kocur
相关产品推荐
相关产品推荐

