Mockito Spy监控Quartz任务的MethodInvokingJobDetailFactoryBean失败问题
Spring 6 + Quartz 定时任务测试失败问题解决
我基于Spring 6和Quartz实现了SimpleTrigger定时任务,核心代码及测试遇到的问题如下:
1. 任务逻辑组件
@Component @Slf4j public class Greeting { public void sayHello() { log.debug("Hello at {}:", LocalDateTime.now()); } }
2. Quartz配置类
@Configuration class QuartzConfig{ @Bean MethodInvokingJobDetailFactoryBean greetingJobDetailFactoryBean() { var jobFactory = new MethodInvokingJobDetailFactoryBean(); jobFactory.setTargetBeanName("greeting"); jobFactory.setTargetMethod("sayHello"); return jobFactory; } @Bean public SimpleTriggerFactoryBean simpleTriggerFactoryBean() { SimpleTriggerFactoryBean simpleTrigger = new SimpleTriggerFactoryBean(); simpleTrigger.setJobDetail(greetingJobDetailFactoryBean().getObject()); simpleTrigger.setStartDelay(1_000); simpleTrigger.setRepeatInterval(5_000); return simpleTrigger; } @Bean public SchedulerFactoryBean schedulerFactoryBean() { var factory = new SchedulerFactoryBean(); factory.setTriggers( simpleTriggerFactoryBean().getObject(), cronTriggerFactoryBean().getObject() ); return factory; } }
3. 测试代码及报错
使用Awaitility结合Mockito Spy编写测试用例验证任务调用次数时,测试失败:
@SpringJUnitConfig(value = { QuartzConfig.class, Greeting.class }) public class GreetingTest { @Autowired Greeting greeting; Greeting greetingSpy; @BeforeEach public void setUp() { this.greetingSpy = spy(greeting); } @Test public void whenWaitTenSecond_thenScheduledIsCalledAtLeastTenTimes() { await() .atMost(Duration.ofSeconds(10)) .untilAsserted(() -> verify(greetingSpy, atLeast(1)).sayHello()); } }
报错信息:
org.awaitility.core.ConditionTimeoutException: Assertion condition defined as a com.example.demo.GreetingTest Wanted but not invoked: greeting.sayHello(); -> at com.example.demo.GreetingTest.lambda$whenWaitTenSecond_thenScheduledIsCalledAtLeastTenTimes$0(GreetingTest.java:36) Actually, there were zero interactions with this mock. within 10 seconds.
问题原因
测试中手动创建的greetingSpy只是原Greeting Bean的包装对象,但Quartz的MethodInvokingJobDetailFactoryBean直接从Spring容器中获取原greeting Bean执行方法,并不会使用测试代码里的Spy对象,因此验证Spy的调用次数始终为0。
解决方案
将Spy对象替换Spring容器中的原Bean,让Quartz任务调用的是Spy实例,修改后的测试类如下:
@SpringJUnitConfig(value = { QuartzConfig.class }) public class GreetingTest { @SpyBean Greeting greeting; @Test public void whenWaitTenSecond_thenScheduledIsCalledAtLeastTenTimes() { await() .atMost(Duration.ofSeconds(10)) .untilAsserted(() -> verify(greeting, atLeast(2)).sayHello()); } }
关键修改点
- 移除手动创建Spy的逻辑,改用
@SpyBean注解:该注解会自动创建原Bean的Spy实例,并替换Spring容器中的原Bean,确保Quartz任务执行时调用的是Spy对象。 - 测试类不再直接引入
Greeting.class,由Spring通过@SpyBean自动创建并管理Spy实例。
修改后即可正确捕获任务对sayHello方法的调用,完成测试验证。
内容的提问来源于stack exchange,提问作者Hantsy
相关产品推荐
相关产品推荐

