You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 18:31:05