Mockito Spy quartz MethodInvokingJobDetailFactoryBean target job bean failed

Viewed 21

Spring 6, Quartz, and a SimpleTrigger based scheduled task.

@Component
@Slf4j
public class Greeting {

    public void sayHello() {
        log.debug("Hello at {}:", LocalDateTime.now());
    }
}

Quartz config:


@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;
    }

And I tried to use awaitility to check the invocations.


@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());
    }
}

Running the tests, it is failed.

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.

In the jobDetailFactorBean, I used jobFactory.setTargetBeanName("greeting"); to setup the target beans here, it should pass the Greeting bean directly.

1 Answers

You're creating a spy that in no way interacts with the actual code:

    @BeforeEach
    public void setUp() {
        this.greetingSpy = spy(greeting);
    }

This would have to be injected into the Spring context as a bean and used everywhere, where greeting is used. Spring actually provides such functionality: @SpyBean.

Instead of autowiring a greeting and wrapping it with a spy that does not interact with anything in the context, replace the @Autowired with @SpyBean annotation. Thanks to that a spy bean will be created and injected within the Spring context:

    @SpyBean
    Greeting greeting;

I created a commit in GitHub repository, where you can see the whole code - the test passes. I had to add the cronTriggerFactoryBean() method to the configuration as it is omitted in your question.

Related