Java @Scheduled: 매시 정각에 실행되는 Cron 표현식 테스트하기

2023. 12. 20. 13:37Project 해축갤/테스트 코드

728x90

배경

    @Transactional
    @Scheduled(cron = "0 0 * * * *")
    public void updatePopularPosts() {
        // 새로운 상위 10개 게시물 추출
        List<Tuple> top10Posts = postRepository.findTop10Posts();

        // 기존의 data 삭제
        popularPostRepository.deleteAllInBatch();

        // 변환 후 새로운 data 삽입
        List<PopularPost> popularPosts = convertToPopularPost(top10Posts);
        popularPostRepository.saveAll(popularPosts);
    }

위 함수에 대한 테스트 코드를 작성해보려 하다 이런 글을 쓰게 되었습니다.

테스트의 목적은 매 정시에 함수가 실제로 호출되는지 여부를 확인하는 것이었습니다.

찾아본 솔루션 2가지

  1. 통합 테스트
  2. Awaitiliy

두 가지 방법 모두 코드 구현에는 차이가 있지만, 궁극적으로는 짧은 시간 간격으로 스케줄링된 함수의 호출 여부를 확인하는 방식입니다.

이는 짧은 주기로 스케줄링된 함수에 대해 효과적인 테스트 방법입니다.

 

그러나 문제는, 제 함수는 매 정시마다 스케줄링되어 있어서, 테스트를 위해 매시간 기다릴 수는 없는 상황입니다. 따라서, 직접적인 함수 호출을 확인하는 대신, @cron 표현식이 제대로 적용되어 매 정시마다 함수를 트리거하는지 여부에 초점을 맞추어 문제를 다시 접근하게 되었습니다.

@cron 표현식 테스트

이 접근 방식은 @Scheduled 어노테이션에 설정된 cron 표현식이 올바르게 구성되어

실행 시간을 정확히 계산하는지 검증하는 데 중점을 둡니다.

실제 함수 호출이 아닌, 예정된 호출 시간이 정확한지 확인함으로써 스케줄링 로직의 정확성을 검증하는 방식입니다.

테스트 구현 방법

  1. Cron 표현식 파싱: 먼저, @Scheduled 어노테이션에 정의된 cron 표현식을 파싱합니다. 이는 스케줄링의 시간 기준을 설정하는 데 사용됩니다.
  2. 예상 실행 시간 계산: 특정 기준 시간(예: 현재 시간)으로부터 cron 표현식에 따라 다음 실행 시간을 계산합니다.
  3. 검증: 계산된 실행 시간이 실제로 cron 표현식에 의해 예상되는 시간과 일치하는지 확인합니다. 이를 통해 스케줄러가 정확히 매 정시마다 함수를 호출하도록 설정되었는지 검증할 수 있습니다.

예상 결과

이 테스트를 통해 updatePopularPosts 메서드가 매 정시마다 올바르게 트리거되는지 확인할 수 있습니다. 실제 시간을 기다리지 않고도 스케줄링 로직의 정확성을 효과적으로 검증하는 방법으로, 특히 크론 표현식의 복잡성을 고려할 때 매우 유용합니다.

구체적인 테스트 코드 예시

class PopularPostServiceTest {

    @Test
    @DisplayName("updatePopularPosts 메서드가 매시 정각에 실행되어야 한다")
    public void shouldTrigger_updatePopularPosts_atEveryHour() throws ParseException {
        // Given - 상황 설정
        // 매시 정각을 의미하는 cron 표현식 설정
        String cronExpression = "0 0 * * * *";
        // Cron 트리거 생성: 주어진 cron 표현식으로 다음 실행 시간을 계산할 객체
        CronTrigger trigger = new CronTrigger(cronExpression);
        // 테스트 시작 시간을 설정함
        Date startTime = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").parse("2023/12/20 00:00:00");
        // 스케줄링 컨텍스트 초기화
        SimpleTriggerContext context = new SimpleTriggerContext();
        context.update(startTime, startTime, startTime);
        // 예상되는 실행 시간 목록
        List<String> expectedTimes = Arrays.asList(
            "2023/12/20 01:00:00",
            "2023/12/20 02:00:00",
            "2023/12/20 03:00:00");

        // When - 동작 수행
        // 각 예상 시간에 대해 cron 표현식에 따라 계산된 다음 실행 시간을 검증
        for (String expectedTime : expectedTimes) {
            Date nextExecutionTime = trigger.nextExecutionTime(context);

            // Then - 결과 검증
            // 계산된 실행 시간이 예상 시간과 일치하는지 확인
            String actualTime = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(nextExecutionTime);
            assertThat(actualTime, is(expectedTime));
            // 다음 예상 시간 계산을 위해 컨텍스트 업데이트
            context.update(nextExecutionTime, nextExecutionTime, nextExecutionTime);
        }
    }
}

이 코드는 @Scheduled 어노테이션에 설정된 cron 표현식이 올바르게 작동하는지 확인하는 테스트를 수행합니다.

주어진 cron 표현식에 따라 예상되는 실행 시간을 계산하고, 이를 실제로 예상된 시간과 비교하여 검증합니다.

결론

이번 글에서 @Scheduled 어노테이션의 cron 표현식이 제대로 설정되었는지 간단히 확인할 수 있었습니다.

이 방식은 실제 함수의 실행을 기다리지 않고도, 예정된 시간에 맞춰 함수가 트리거되는지를

빠르게 검증할 수 있는 효과적인 방법입니다.

 

이러한 접근 방식을 사용하면, 크론 스케줄링 로직이 정확하게 작동하는지를 효율적으로 확인할 수 있어,

개발 과정에서 시간을 절약하고 신뢰성을 높일 수 있습니다.

참고

How to Test the @Scheduled Annotation | Baeldung

Spring Scheduler 테스트 하기

Spring의 @Scheduled annotation으로 설정된 cron expression을 테스트하기 - 개발수양록

728x90