it-swarm.com.de

So testen Sie Spring @ Scheduled

Wie teste ich @Scheduled job-Aufgaben in meiner Spring-Boot-Anwendung?

 package com.myco.tasks;

 public class MyTask {
     @Scheduled(fixedRate=1000)
     public void work() {
         // task execution logic
     }
 }
14
S Puddin

Wenn wir davon ausgehen, dass Ihr Job in so kleinen Intervallen ausgeführt wird, dass der Test wirklich auf die Ausführung des Jobs warten soll und Sie nur testen möchten, ob der Job aufgerufen wird, können Sie die folgende Lösung verwenden:

Awaitility zum Klassenpfad hinzufügen:

<dependency>
    <groupId>org.awaitility</groupId>
    <artifactId>awaitility</artifactId>
    <version>3.1.0</version>
    <scope>test</scope>
</dependency>

Schreibtest ähnlich wie:

@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {

    @SpyBean
    private MyTask myTask;

    @Test
    public void jobRuns() {
        await().atMost(Duration.FIVE_SECONDS)
               .untilAsserted(() -> verify(myTask, times(1)).work());
    }
}
14

Meine Frage ist: "Was willst du testen?"

Wenn Ihre Antwort lautet "Ich möchte wissen, dass Spring meine geplante Aufgabe zum gewünschten Zeitpunkt ausführt", testen Sie Spring und nicht Ihren Code. Dies ist nicht etwas, was Sie zum Unit-Test benötigen.

Wenn Ihre Antwort lautet "Ich möchte wissen, dass ich meine Aufgabe richtig konfiguriert habe", schreiben Sie eine Test-App mit einer häufig ausgeführten Aufgabe und überprüfen Sie, ob die Aufgabe ausgeführt wird, wenn Sie damit rechnen. Dies ist kein Komponententest, zeigt jedoch, dass Sie wissen, wie Sie Ihren Text richtig konfigurieren.

Wenn die Antwort "Ich möchte wissen, dass die von mir geschriebene Aufgabe korrekt funktioniert" lautet, müssen Sie die Aufgabenmethode in einem Unit-Test testen. In Ihrem Beispiel möchten Sie die work() -Methode einem Komponententest unterziehen. Schreiben Sie dazu einen Unit-Test, der Ihre Task-Methode (work()) direkt aufruft. Zum Beispiel,

public class TestMyTask
{
  @InjectMocks
  private MyTask classToTest;

  // Declare any mocks you need.
  @Mock
  private Blammy mockBlammy;

  @Before
  public void preTestSetup()
  {
    MockitoAnnotations.initMocks(this);

    ... any other setup you need.
  }

  @Test
  public void work_success()
  {
    ... setup for the test.


    classToTest.work();


    .. asserts to verify that the work method functioned correctly.
  }
3
DwB

Das ist oft schwer. Sie können erwägen, den Spring-Kontext während des Tests zu laden und einige Bean daraus zu fälschen, um den geplanten Aufruf überprüfen zu können.

Ich habe ein solches Beispiel in meinem Repo Github. Es wird ein einfaches geplantes Beispiel mit der beschriebenen Vorgehensweise getestet.

2
luboskrnac

diese Klasse steht für das Generieren von Schedulern mit Springframework-Scheduling

import org.Apache.log4j.Logger;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.scheduling.support.CronSequenceGenerator;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@RunWith(SpringJUnit4ClassRunner.class)
@Configuration
@PropertySource("classpath:application.properties")
public class TrimestralReportSenderJobTest extends AbstractJUnit4SpringContextTests {

    protected Logger LOG = Logger.getLogger(getClass());

    private static final String DATE_CURRENT_2018_01_01 = "2018-01-01";
    private static final String SCHEDULER_TWO_MIN_PERIOD = "2 0/2 * * * *";
    private static final String SCHEDULER_QUARTER_SEASON_PERIOD = "0 0 20 1-7 1,4,7,10 FRI";

    @Test
    public void cronSchedulerGenerator_0() {
        cronSchedulerGenerator(SCHEDULER_QUARTER_SEASON_PERIOD, 100);
    }

    @Test
    public void cronSchedulerGenerator_1() {
        cronSchedulerGenerator(SCHEDULER_TWO_MIN_PERIOD, 200);
    }

    public void cronSchedulerGenerator(String paramScheduler, int index) {
        CronSequenceGenerator cronGen = new CronSequenceGenerator(paramScheduler);
        Java.util.Date date = Java.sql.Date.valueOf(DATE_CURRENT_2018_01_01);

        for (int i = 0; i < index; i++) {
            date = cronGen.next(date);
            LOG.info(new Java.text.SimpleDateFormat("EEE, MMM d, yyyy 'at' hh:mm:ss a").format(date));
        }

    }
}

hier ist die Ausgabeprotokollierung:

<com.medici.scheduler.jobs.TrimestralReportSenderJobTest> - lun, gen 1, 2018 at 12:02:02 AM
<com.medici.scheduler.jobs.TrimestralReportSenderJobTest> - lun, gen 1, 2018 at 03:02:02 AM
<com.medici.scheduler.jobs.TrimestralReportSenderJobTest> - lun, gen 1, 2018 at 06:02:02 AM
<com.medici.scheduler.jobs.TrimestralReportSenderJobTest> - lun, gen 1, 2018 at 09:02:02 AM
<com.medici.scheduler.jobs.TrimestralReportSenderJobTest> - lun, gen 1, 2018 at 12:02:02 PM
1
Tiago Medici

Wir können mindestens zwei Ansätze verwenden, um geplante Aufgaben mit Spring zu testen:

  • Integrationstest

Wenn wir Spring Boot verwenden, benötigen wir folgende Abhängigkeiten:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
</dependency>

Wir könnten ein count zum Task hinzufügen und es innerhalb der work-Methode erhöhen:

 public class MyTask {
   private final AtomicInteger count = new AtomicInteger(0);

   @Scheduled(fixedRate=1000)
   public void work(){
     this.count.incrementAndGet();
   }

   public int getInvocationCount() {
    return this.count.get();
   }
 }

Dann überprüfen Sie die count:

@SpringJUnitConfig(ScheduledConfig.class)
public class ScheduledIntegrationTest {

    @Autowired
    MyTask task;

    @Test
    public void givenSleepBy100ms_whenWork_thenInvocationCountIsGreaterThanZero() 
      throws InterruptedException {
        Thread.sleep(2000L);

        assertThat(task.getInvocationCount()).isGreaterThan(0);
    }
}
  • Eine andere Alternative ist die Verwendung von Awaitility wie erwähnt @ maciej-walkowiak. 

In diesem Fall müssen wir die Awaitility-Abhängigkeit hinzufügen:

<dependency>
    <groupId>org.awaitility</groupId>
    <artifactId>awaitility</artifactId>
    <version>3.1.6</version>
    <scope>test</scope>
</dependency>

Und überprüfen Sie mit DSL die Anzahl der Aufrufe der Methode work:

@SpringJUnitConfig(ScheduledConfig.class)
public class ScheduledAwaitilityIntegrationTest {

    @SpyBean 
    MyTask task;

    @Test
    public void whenWaitOneSecond_thenWorkIsCalledAtLeastThreeTimes() {
        await()
          .atMost(Duration.FIVE_SECONDS)
          .untilAsserted(() -> verify(task, atLeast(3)).work());
    }
}

Wir müssen berücksichtigen, dass, obwohl sie gut sind, es ist besser, sich auf die Unit-Tests der Logik innerhalb der Arbeitsmethode zu konzentrieren.

Ich habe ein Beispiel hier .

0
SHoko