scheduled

How to Use and Test @Scheduled Annotation in Spring

In this article we will learn how to use and test Scheduled annotation in Spring.

Let’s start!

Introduction

Scheduled annotation in Spring is quite helpful when you need to start a job or task every certain time. This annotation can help you saving some time writing a Runnable task and using ScheduledExecutorService to periodically run your task.

Spring code is throughly tested as well, so the chances of making a mistake are lower when using this annotation.

Let’s see how can we use it!

Usage

Using Scheduled annotation is quite simple, we just have to annotate our method with it and specify some parameters to tell Spring when do we want to start our job and how frequently should it run.

One thing to keep in mind about this annotation is that its retention policy is RUNTIME, therefore the annotation is still present in our compiled code. It will be processed at runtime by the corresponding annotation processor.

In this case, we don’t think that performance should be a concern, as this will be used just to start a task and won’t be part of any user journey request. But every time you use an annotation please try to understand when it will be processed and what the implications are for your application.

You can read more about the problems around “annotation-driven development” in our article “The Problems of Annotation-Driven Development”.

Now that we’ve mentioned this, let’s look at a few examples to understand how can we configure this annotation.

Enable Scheduling

The very first thing we should do is to enable scheduling in our Spring application. This is as simple as adding EnableScheduling annotation to our configuration bean.

For instance, we could have the following:

@Configuration
@EnableScheduling
public class MyAppConfiguration {
    ...
}

Once we have this, Spring should be able to process Scheduled annotations and trigger our tasks in the way we’ve configure them.

Fixed Delay

One of the parameters that we can configure is fixedDelay parameter. This method accepts an integer representing the number of milliseconds between each execution.

In each of our example we are going to use a scheduler component that will be triggered based on our annotation configuration. For instance, we could have this:

@Component
public class MyScheduler {

    private final MyService myService;

    @Scheduled(fixedDelay = 5000)
    public void run() {
        myService.call();
    }
}

In this example, we’d be executing our scheduler 5 seconds after the previous run completed. In our scheduler we’d be calling a theoretical MyService component.

We’ll see later how to test this. For now, let’s continue with the configuration parameters.

Initial Delay

We can also set an initial delay for our task if we don’t want it to start immediately after application startup. For example, if we wanted to start our task thirty seconds after startup, we could do this:

@Component
public class MyScheduler {

    private final MyService myService;

    @Scheduled(fixedDelay = 5000, initialDelay = 5000)
    public void run() {
        myService.call();
    }
}

In this case, we won’t be running immediately after startup. We’ll be waiting for 5 seconds and then, once the first run gets completed, we’ll start another job after 5 seconds.

Simple, right?

Cron Expressions

Another way to define when to run our jobs is by using cron expressions. Cron expressions are very powerful and you can configure almost anything with them.

For example, if we wanted to run our job every thirty minutes, we could do something like this:

@Component
public class MyScheduler {

    private final MyService myService;

    @Scheduled(cron = "0 0/30 * * * ?")
    public void run() {
        myService.call();
    }
}

Let’s see now one more way to configure scheduled annotations!

Configuration Property

A very useful way to configure this annotation is by using a property name configured in our Spring properties.

If we modify our example to use properties, it’ll look like this:

@Component
public class MyScheduler {

    private final MyService myService;

    @Scheduled(fixedDelayString = "${app.scheduler.delay-ms}")
    public void run() {
        myService.call();
    }
}

We’ve now covered the main options you will need to configure Scheduler annotation. What about testing this? Let’s see how to test it!

scheduled annotation
Photo by Jessica Lewis on Unsplash

Testing

We’re going to write a simple integration test using Spring Boot. In this test we will be testing that our scheduler is started within the period defined in our test property. Therefore, we will use the latest example in the previous section.

In this way we’d be testing that the annotation is being read correctly and also that Spring is being able to read this annotation and start the job accordingly.

Let’s see how our test would look like then!

@ExtendWith(SpringExtension.class)
@SpringBootTest(
        classes = { MyApplication.class, MySchedulerIntegrationTest.MySchedulerTestConfig.class },
        properties = {
                "app.scheduler.delay-ms=200"
        })
@ActiveProfiles("local")
class MySchedulerIntegrationTest {

    @MockBean
    private MyService myService; // Dependency injected into scheduler, we mock it to verify is being called.

    @Test
    void shouldStartScheduler() {

        await().atMost(300, TimeUnit.MILLISECONDS)
                .untilAsserted(() -> {
                    verify(myService, times(2)).call();
                });
    }

    @Configuration
    static class MySchedulerTestConfig {
       // If you need to mock anything in your context, add here using @MockBean.
    }
}

As you can see, we are defining a delay of 200 milliseconds between executions. Our expectation is then that myService.call is called twice in a period of 300 milliseconds. The first time right after startup, and the second time 200 milliseconds later.

Is that clear? We hope so.

If you need to enable or disable your scheduler based on a property, check our article “How to Use and Test ConditionalOnProperty annotation in Spring Boot”.

That’s all! In this way your scheduler would be tested, but please keep in mind that you will need a proper integration test or functional test checking that the real MyService component is called and it integrated properly.

Conclusion

In this article we’ve learned how to use and test @Scheduled annotation in Spring. This annotation simplifies things when we want to run a background job periodically in our Spring application.

If you’re interested in reading more of our Spring articles, you can find them here.

That’s all from us today! Hoping that you found this article useful and looking forward to seeing you again very soon.

Thanks for reading us!