Groovy Spock测试Mock @PostConstruct内调用失败,求解决方案
问题分析与解决方案
你踩的这个坑其实很常见——问题核心在于**@PostConstruct方法的执行时机早于你设置Mock的时机**。
当Spring容器初始化ServiceImpl实例时,会立刻调用标注了@PostConstruct的init()方法;而你在测试方法的given块里才给abcDao设置Mock返回值,这时候init()早就执行完了,abcDao.findByName("testString")返回的还是null,自然触发Assert.notNull的失败断言。
下面给你两种可行的解决思路:
方案一:用@MockBean提前注入Mock并配置返回值
如果你的测试基于Spring Boot,可以用@MockBean注解让Spring容器加载时就注入Mock版本的AbcDao,并在测试初始化阶段就配置好返回值,确保@PostConstruct执行前Mock已经准备就绪:
@SpringBootTest class ServiceSpec extends Specification { @MockBean AbcDao abcDao // Spring会把这个Mock实例注入到ServiceImpl中 @Autowired Service service def setup() { // 在setup阶段(Spring上下文初始化后,测试方法执行前)配置Mock返回值 Dao testDao = new Dao() given(abcDao.findByName("testString")).willReturn(testDao) } def 'Test method'() { when: service.toBeTest() then: noExceptionThrown() // 可添加其他业务逻辑断言 } }
方案二:手动管理Service实例的初始化流程
如果不需要加载完整的Spring上下文,可以手动创建ServiceImpl实例,注入Mock的AbcDao后,再手动调用init()方法,完全控制执行顺序:
class ServiceSpec extends Specification { def 'Test method'() { given: // 先创建并配置Mock的AbcDao AbcDao abcDao = Mock(AbcDao) Dao testDao = new Dao() abcDao.findByName("testString") >> testDao // 手动创建Service实例,注入Mock依赖,再调用init ServiceImpl service = new ServiceImpl() service.abcDao = abcDao service.init() // 此时Mock已经配置好,init能拿到预期的testDao when: service.toBeTest() then: noExceptionThrown() } }
两种方案的核心都是:确保Mock的abcDao在init()方法执行前,就已经配置好了正确的返回值,这样testDao就能被正常赋值,不会触发断言失败。
内容的提问来源于stack exchange,提问作者Techno
相关产品推荐
相关产品推荐

