테스트에서 Spring Application Context를 한 번만 띄워보자

JeongYong Park·2023년 8월 30일
1

현재 프로젝트에서 애플리케이션 레이어 - 레포지토리 레이어 테스트코드를 작성할 때 @SpringBootTest를 사용하고 있습니다. 그런데 @MockBean이 있는 테스트 클래스마다 SpringApplicationContext 를 매번 띄우고 있는 상황이 발생하고 있었습니다.

왜 이런 상황이 발생하는지 기록하기 위해 이번 포스팅을 작성합니다.

Spring의 컨텍스트 관리 및 캐싱

공식문서에 따르면 Spring TestContext Framework는 Spring ApplicationContext 인스턴스 및 WebApplicationContext 인스턴스의 일관된 로딩과 해당 컨텍스트의 캐싱을 제공합니다.

이런 컨텍스트의 캐싱을 제공하는 이유는 최초로 스프링 컨텍스트를 띄우는 시간이 문제가 될 수 있기 때문입니다. 우리가 만들어 놓은 모든 빈들이 빈 컨테이너에 등록되고 스프링 부트가 제공해주는 모든 빈들이 등록되기 때문입니다. 이렇게 되면 모든 테스트를 실행하기 전에 컨텍스트를 띄우는 비용이 발생하게 되고 테스트 실행속도가 느려지게 됩니다.

그런데 똑똑한 스프링은 컨텍스트를 캐싱해두고 재사용하게 됩니다.

기본적으로 스프링 컨텍스트가 일단 로드되면 구성된 내용들은 각 테스트에 재사용되게 됩니다. 따라서 최초 설정 비용은 한 번만 발생하게 되는 것입니다.

그런데 왜 @MockBean 애노테이션이 있을 때 스프링 컨텍스트는 다시 띄워지는 것일까요?

왜?

공식문서에 따르면 테스트가 애플리케이션 컨텍스트를 손상시키고 다시 로드해야 하는 경우, (예: Bean 정의 또는 애플리케이션 객체의 상태를 수정) Spring TestContext Framework는 다음 테스트를 위해 구성을 다시 로드하고 애플리케이션 컨텍스트를 다시 빌드하도록 구성합니다.

@MockBean을 사용해 빈을 새롭게 정의했기 때문에 컨텍스트가 손상되었고 이를 감지한 스프링이 컨텍스트를 다시 띄우는 것이었습니다.

한 번만 띄우도록 수정

현재 각 테스트 클래스들은 필요한 빈들을 @Autowired 받고 있습니다.

@ApplicationTest
class AuthServiceTest {

    @Autowired
    private AuthService authService;

    @Autowired
    private SupportRepository supportRepository;

    @MockBean
    private NaverRequester naverRequester;
...
@ApplicationTest
public class CategoryServiceTest {

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private SupportRepository supportRepository;
...
@ApplicationTest
class ItemServiceTest {

    @MockBean
    private S3Uploader s3Uploader;

    @Autowired
    private SupportRepository supportRepository;

    @Autowired
    private ItemService itemService;
...

이외에도 각 테스트 클래스들은 제어할 수 없는 것에 대해서는 @MockBean 처리를 해두고 있고 필요한 빈들은 @Autowired를 통해 주입받고 있습니다.

어차피 제어할 수 없는 것들은 @MockBean 처리할 것이기 때문에 이를 공통 필드로 가지고 있는 클래스를 하나 생성한 후 각 테스트 클래스들은 공통 필드를 가지고 있는 클래스를 상속받도록 합니다.

@ApplicationTest
public class ApplicationTestSupport {

    @MockBean
    protected S3Uploader s3Uploader;
    
    @MockBean
    protected NaverRequester naverRequester;
    
    @Autowired
    protected SupportRepository supportRepository;

...
@ApplicationTest
class ItemServiceTest extends ApplicationTestSupport {

    @Autowired
    private ItemService itemService;
...

결론

이렇게 스프링 컨텍스트가 띄워질 때 목처리할 빈들을 미리 만들어두게 되면서 스프링 애플리케이션 컨텍스트가 테스트 시에 한 번만 띄워지게 되었습니다!

참고 자료

https://stackoverflow.com/questions/22939226/how-can-i-initialize-a-spring-applicationcontext-just-once-for-all-tests

https://docs.spring.io/spring-framework/reference/testing/integration.html#testing-ctx-management

profile
다음 단계를 고민하려고 노력하는 사람입니다

0개의 댓글