비동기 로직 동기 방식으로 테스트하기

공병주(Chris)·2022년 11월 17일
2
post-thumbnail

속닥속닥의 알림 기능을 비동기 이벤트 방식으로 풀어내고 테스트를 하는 과정에서 비동기에 대한 테스트를 하기 어려운 문제를 맞이했다. 따라서, 테스트 환경은 동기 방식으로 진행했는데 방법을 공유하려고 한다.

https://velog.io/@byeongju/%EC%86%8D%EB%8B%A5%EC%86%8D%EB%8B%A5-%EC%95%8C%EB%A6%BC-%EA%B8%B0%EB%8A%A5-%EA%B0%9C%EC%84%A0%EA%B8%B0-1
테스트를 동기로 가져간 이유는 아래의 글의 테스트 부분을 읽어보면 좋을 것 같다.

방법은 꽤 간단하다. 아래의 코드는 비동기 처리를 위한 ThreadPoolTaskExecutor 를 Bean으로 등록하는 Configuration이다. 정확히 말하면 ThreadPoolTaskExecutor가 비동기를 Task를 처리하는 것은 아니고, ThreadPoolExecutor가 이를 처리하는데 ThreadPoolExecutor를 관리하고 모니터링하는 객체이다. ThreadPoolTaskExecutor는 내부적으로 ThreadPoolExecutor를 가지고 있다.

다시 본론으로 돌아와서, @SpringBootTest 를 통해 해당 Bean이 초기화되는데 이를 다른 Bean으로 바꿔치기 하는 방식을 사용하면 된다.

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {

    @Override
    @Bean(name = "asyncExecutor")
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(CORE_POOL_SIZE);
        executor.setMaxPoolSize(MAX_POOL_SIZE);
        executor.setQueueCapacity(QUEUE_CAPACITY);
        executor.setThreadNamePrefix(THREAD_NAME_PREFIX);
        executor.initialize();
        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return AsyncConfigurer.super.getAsyncUncaughtExceptionHandler();
    }
}

출처 : https://stackoverflow.com/questions/29743320/how-exactly-does-the-spring-beanpostprocessor-work

위의 그림에서 Bean Post Processors를 보면 Bean이 초기화되기 전후로 특정 작업을 하는 것으로 보인다.

여기서 아래와 같이 빈으로 등록되는 ThreadPoolTaskExecutor를 SyncTaskExecutor로 바꾸는 것이다.

@TestConfiguration
public class AsyncTestConfig {

    @Bean
    public AsyncExecutorPostProcessor asyncExecutorPostProcessor() {
        return new AsyncExecutorPostProcessor();
    }

    static class AsyncExecutorPostProcessor implements BeanPostProcessor {

        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            if (beanName.equals("asyncExecutor")) {
                return new SyncTaskExecutor();
            }
            return bean;
        }
    }
}

위와 같이 BeanPostProcessor의 구현체를 빈으로 등록하고 postProcessAfterInitialization를 구현하면 된다.

ApplicationContext가 load 되는 과정에서, Bean들이 postProcessAfterInitialization에 매개변수로 들어온다. 들어오는 Bean 중에 비동기 처리를 위한 Thread Pool을 담당하는 빈의 이름일 경우, SyncTaskExecutor라는 동기 방식의 SyncTaskExecutor로 바꿔주면 된다.

끗.

참고자료

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/config/BeanPostProcessor.html

도움주신분

우아한테크코스 4기 더즈

0개의 댓글