들어가며
요즘 테스트 코드의 중요성이 부각되며 테스트 코드들을 많이 작성하실 것 같습니다.
혹시 통합 테스트코드를 작성하기 위해 다음과 같이 작성하셨다면 오늘 글이 도움이 되길 바라며 적어봅니다.
@SpringBootTest
class MemberServiceTest {
// test code ..
}
@SpringBootTest
class PostServiceTest {
// test code ..
}
Spring TestContext Framework
Spring TestContext Framework는 Spring Application 테스트를 위한 핵심 기능들을 제공합니다.
- Context Management
- Dependency Injection of Test Fixtures
- Transaction Management
- Parallel Test Execution
이 외에도 다양한 기능들을 제공합니다.
이 중 오늘은 Context Management과 관련하여 Context Caching에 대해서 알아보고자 합니다.
Context Caching
테스트 컨텍스트 프레임워크는 테스트 실행 시 ApplicationContext의 구성을 기반으로 캐시 하게 됩니다.
그 후의 테스트에서 동일한 구성을 사용하면 ApplicationContext를 생성하지 않고, 재사용하는 방식을 채택합니다.
이러한 방식은 테스트의 실행 시간을 줄일 수 있다는 장점이 존재합니다.
Application Context의 생성 비용
ApplicationContext를 생성하는데 다음과 같은 작업들이 포함되기 때문에 시간이 오래 걸립니다.
- 설정 파일 로딩
- Bean Definition 해석
- 의존성 주입
- 초기화 콜백 실행
- 그 외 설정
// ApplicationContext 생성 X
@Test
void nonApplicationContextTest() {
ClassA classA = new ClassA(10);
}
// ApplicationContext 생성
@Test
void applicationContextTest() {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(InnerConfig.class);
ClassA classA = ac.getBean("classA", ClassA.class);
}
static class ClassA {
private int number;
public ClassA(int number) {
this.number = number;
}
}
@Configuration
static class InnerConfig {
@Bean
ClassA classA() {
return new ClassA(10);
}
}
총 두 가지의 테스트를 준비했습니다.
- nonApplicationContextTest
- ApplicationContext를 별도로 생성하지 않는 테스트
- applicationContextTest
- ApplicationContext를 생성하는 테스트
사진에서 확인할 수 있듯 ApplicationContext를 생성하는 테스트의 경우 상당한 시간이 소요됩니다.
Application Context 생성
만약 기존에 사용하던 Application Context와 다른 구성을 하게 된다면 ApplicationContext는 재생성됩니다.
이러한 경우가 주로 발생하는 예시 중 하나가 @MockBean일 것 같습니다.
@SpringBootTest
@Transactional
public abstract class IntegrationTestSupport {
}
class SchedulerTest extends IntegrationTestSupport {
@MockBean
private AlarmRepository alarmRepository;
@MockBean
private WebhookConnectionTester webhookConnectionTester;
// ...
}
실제로 제가 작성했던 테스트코드 중 일부입니다.
Alarm의 경우 Slack API와 연동되어 있기 때문에 실제로 빈을 등록하지 않고, @MockBean으로 처리하여 테스트코드를 작성했습니다.
./gradlew clean test --info를 통해 테스트를 확인해 보니 ApplicationContext가 재생성되는 것을 확인할 수 있었습니다.
일반적으로 스프링은 @MockBean 설정이 다르거나, @MockBean이 존재하지 않는다면 다른 구성으로 판단합니다.
그렇기 때문에 새로 ApplicationContext를 생성하게 됩니다.
환경 통합 방법
만약 특정 테스트에서 @MockBean 처리 혹은 특수 환경 설정이 필요한 것이 아니라면
테스트 환경 통합을 위한 추상클래스를 하나 만들어 둔 뒤에, 추상클래스에서 공통 처리를 해주면 환경 통합이 가능합니다.
@SpringBootTest
@Transactional
public abstract class IntegrationTestSupport {
@MockBean
protected Notifier notifier;
@MockBean
protected AlarmRepository alarmRepository;
@MockBean
protected WebhookConnectionTester webhookConnectionTester;
@MockBean
protected MemberUpdateHandler memberUpdateHandler;
}
class SchedulerTest extends IntegrationTestSupport {
@Autowired
private Scheduler scheduler;
// ...
}
앞선 코드의 예시와 달리 이번에는 추상클래스에 @MockBean처리를 해 두었고, 나머지 테스트 클래스들의 경우에는 Bean과 관련해 별도의 설정을 하지 않도록 구성했습니다.
결과
환경 통합 전 | 환경 통합 후 | |
ApplicationContext 생성 횟수 | 6 | 2 |
테스트 실행 시간 | 1sec 576ms | 1sec 166ms |
테스트 환경 통합 후 ApplicationContext의 생성 횟수가 줄음과 동시에 실행시간도 감축된 것을 확인할 수 있습니다.
(현재 테스트의 경우 ControllerTest는 @WebMvcTest를 사용하기 때문에 두 가지의 ApplicationContext가 생성되었습니다.)
참고하면 좋은 Text
'Spring' 카테고리의 다른 글
[Spring] @Retryable, Event 적용기 (1) | 2023.12.04 |
---|---|
Spring, 그리고 Test (4) | 2023.10.11 |
Spring MVC - 요청과 관련된 사용법 정리 (0) | 2023.07.06 |
Junit에서 TestInstance의 생명주기 (0) | 2023.07.03 |
NamedParameterJdbcTemplate (0) | 2023.07.01 |