Quartz 설정을 읽어서 CronExpression 테스트 by 정상혁

 Cron expression은 실수하기가 쉽고, 오류가 뒤늦게서야 발견됩니다. 매일 5시 20분, 40분,60분에 실행될 일정을 지정하고자 했는데 "* 0/20 5 * * * ?"로 써야 할 표현식을  "* 0,20 5 * * * ?"으로 써놓고는 실운영 서버에 배포해서 하루가 지난 다음에 실행결과를 보고서야 실수를 발견한 경험을 해보신 분들이 많으실 것입니다.

 Cron e-pression도 테스트 코드를 짜서 검증을 해본다면 치명적인 실수를 막을 기회가 더 많아집니다.

 

 아래 예제는  ApplicationContext에 설정되어 있는 Quartz의 CronTrigger의 일정을 테스트하는 코드입니다.

ApplicationContext의 스케쥴링 설정 파일


<bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
     <property name="triggers" ref="triggers"/>
</bean>
<util:list id="triggers">
     <bean p:jobName="baseballJob" p:cronE-pression="0 * * * * ?" parent="jobTrigger"/> 
     <bean p:jobName="baseballExportJob" p:cronE-pression="30 5-7 * * * ?" parent="jobTrigger"/>
</util:list>



위의 설정 예제에서는 반복되는 설정을 간편하게 해주는 FactoryBean을 썼습니다. parent="jobTrigger"라고 지정된 부분이 FactoryBean 클래스와 연결되는 bean id를 지정한 속성입니다. 여기에 쓰인 FactoryBean은 이 포스트의 핵심이 아니라서 생략했는데, "jobName"과 "cronE-pression" 속성을 받아서 org.quartz.CronTrigger 타입의 bean을 생성해주는 역할을 합니다. (전체 소스는 JobTriggerFactoryBean.java )

테스트 코드

   triggers라는 bean 이름으로 List<CronTrigger> type의 객체를 가지고 와서, List안에서 지정된 trigger의 이름을 탐색한다음에 그 안에서 그 trigger의 cron e-pression을 검사했습니다.  테스트 코드를 간결하게 유지하기 위해서 Cron e-pression을 검사하는 코드는  QuartzCronE-pressionTestUtils라는 클래스로 분리해서 static import로 처리했습니다.
   QuartzCronExpressionTestUtils.findTriggerByName 메소드는 List<CronTrigger> 타입의 객체가 담고 있는 여러개의 CronTrigger에서 지정된 이름의 CronTrigger를 반환해줍니다.


package edu.batch.baseball.schedule;

import static edu.batch.support.launch.QuartzCronE-pressionTestUtils.*;

import java.text.ParseException;
import java.util.Arrays;
import java.util.List;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.quartz.CronTrigger;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration( { "classpath:/launch-context.xml" })
public class BaseballQuartzScheduleTest {

    private static final String DATE_PATTERN = "yyyy/MM/dd hh:mm:ss";
   
    @Resource(name = "triggers")
    List<CronTrigger> triggers;

    @Test
    public void testbaseballJobTriggerSchedule() throws ParseException {
        CronTrigger trigger = findTriggerByName(triggers, "baseballJobTrigger");
        String initialTime = "2010/09/01 09:00:00";
        List<String> expectedTimeList = Arrays.asList(
                "2010/09/01 09:01:00",
                "2010/09/01 09:02:00",
                "2010/09/01 09:03:00",
                "2010/09/01 09:04:00");
        assertSchedule(trigger, initialTime, expectedTimeList, DATE_PATTERN);
    }
   
    @Test
    public void testbaseballExportJobTriggerSchedule() throws ParseException {
        CronTrigger trigger = findTriggerByName(triggers, "baseballExportJobTrigger");
        String initialTime = "2010/09/01 09:00:00";
        List<String> expectedTimeList = Arrays.asList(
                "2010/09/01 09:05:30",
                "2010/09/01 09:06:30",
                "2010/09/01 09:07:30",
                "2010/09/01 10:05:30",
                "2010/09/01 10:06:30",
                "2010/09/01 10:07:30");
        assertSchedule(trigger, initialTime, expectedTimeList, DATE_PATTERN);
    }
}


 

QuartzCronE-pressionTestUtils

첨부파일 :  QuartzCronExpressionTestUtils.java

 

package edu.batch.support.launch;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import java.text.ParseException;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.commons.lang.time.DateUtils;
import org.quartz.CronTrigger;

public class QuartzCronExpressionTestUtils {
 
 public static void assertSchedule(CronTrigger trigger, String initialTime,
   List<String> expectedTimeList, String datePattern) throws ParseException {
   Date previousStartTime = DateUtils.parseDate(initialTime, new String[]{datePattern});
   
   for(String expectedTime : expectedTimeList){
    trigger.setStartTime(previousStartTime);
    Date nextExecutionTime =  trigger.getFireTimeAfter(previousStartTime);
    String actualTime = DateFormatUtils.format(nextExecutionTime, datePattern);
    assertThat("executed on expected time", actualTime, is(expectedTime));
    previousStartTime = nextExecutionTime;
   }  
  }

 public static CronTrigger findTriggerByName(List<CronTrigger> triggers, String triggerName) {
  for (CronTrigger trigger : triggers) {
   if (triggerName.equals(trigger.getName())) {
    return trigger;
   }
  }
  throw new IllegalArgumentException("cannot find trigger : "
    + triggerName);
 }
}

 


덧글

댓글 입력 영역