본문 바로가기
Project Trouble Shooting/[EATceed] 몸무게 증량 어플

ApplicationContext Caching을 활용한 테스트 환경 개선

by Big Sun 2024. 7. 23.
728x90

 

배경

프로젝트가 진행되면서 Jacoco를 도입해 테스트 커버리지를 측정하고, 처참한 커버리지를 보여줌으로써 팀원들(저 포함..)이 테스트를 작성하도록 독려했습니다. 이를 통해 테스트 코드의 수가 점차적으로 늘어나고 있었습니다.  또한, 테스트 컨테이너를 활용하여 테스트 환경을 구축하는 데 힘을 쏟았습니다.

 

어느덧, 테스트의 수가 70개가 넘어갈 때쯤 Run All Tests할 때 걸리는 시간이 꽤나 늘어나 있었습니다.

 

ApplicationContext Caching

 

스프링에서는 테스트에 사용되는 애플리케이션 컨텍스트를 생성하고 관리하여 테스트에 적용해주는 테스트 프레임워크를 제공합니다. 

테스트 컨텍스트 프레임워크는 테스트가 사용하는 컨텍스트를 캐싱해서 여러 테스트가 컨텍스트를 공유하는 방법을 제공합니다.


하지만, 현재 개발집의 테스트는 컨텍스트 캐싱을 잘 활용하지 못 하고 있었습니다.

따라서, 테스트들이 컨텍스트 캐싱을 효율적으로 활용하도록 코드를 개선하고자 합니다.

 

ApplicationContext가 생성될 때, 빈을 생성하고 의존 관계를 주입하고 스레드를 생성하는 등 꽤나 많은 비용이 들어갑니다.
따라서, ApplicationContext를 테스트마다 생성하는 것은 너무나 많은 비용이 듭니다.

 

 

그렇다면, 현재 ApplicatoinContext는 몇 개 띄워지고 있을까?

 

 

- size : 캐시에 저장되어있는 컨텍스트 수
- hitCount : 애플리케이션 컨텍스트에 접근하는 횟수
- missCount : 캐시에 저장되어있는 컨텍스트를 사용하지 못 한 횟수

 

현재 missCount가 13인 것으로 보아, ApplicationContext의 Caching을 잘 활용하지 못 하고 있습니다.

 

 

언제 ApplicatoinContext가 새로 띄워지는 걸까?

 


통합테스트에서 웹 계층 테스트로 넘어갈 때 ApplicationContext가 새로 띄어지는 것을 확인할 수 있습니다.

 

 

 

현재까지 통합테스트에서는 컨텍스트를 재활용하고 있는 것을 확인할 수 있습니다.

 

 

그러나, 웹 계층 테스트 즉 @WebMvcTest를 사용한 부분에서 새롭게 Application Context를 띄우는 것을 확인할 수 있습니다.

 

정확히 어떤 상황에서 일어나는 걸까?

 

 

공식 문서에 따르면, 구성 매개변수의 고유한 조합은 컨텍스트가 캐시되는 키를 생성하는 데 사용되고, TestContext 프레임워크는 다음 구성 매개변수를 사용하여 컨텍스트 캐시 키를 빌드한다고 합니다.

 

  • locations(에서 @ContextConfiguration)
  • classes(에서 @ContextConfiguration)
  • contextInitializerClasses(에서 @ContextConfiguration)
  • contextCustomizers(에서 ContextCustomizerFactory) – 여기에는 @DynamicPropertySourceSpring Boot 테스트 지원의 다양한 기능뿐만 아니라 메서드도 @MockBean포함 됩니다 @SpyBean.
  • contextLoader(에서 @ContextConfiguration)
  • parent(에서 @ContextHierarchy)
  • activeProfiles(에서 @ActiveProfiles)
  • propertySourceDescriptors(에서 @TestPropertySource)
  • propertySourceProperties(에서 @TestPropertySource)
  • resourceBasePath(에서 @WebAppConfiguration)

즉, 해당 조건 중 하나라도 달라지면 새로운 ApplicationContext를 생성합니다.

 

분석

 

개발집의 테스트를 확인해본 결과 @WebMvcTest의 경우는 @MockBean으로 인해 같은 @WebMvcTest라도 ApplicationContext를 재활용하지 못 하고 있었고, @DataJpaTest와 @SpringBootTest는 애초에 스캔하는 Bean이 달라서 서로 ApplicationContext를 재활용하지 못하고 있었습니다.

- OnBoardingMemberIntegrationTest
- UpdateMemberIntegrationTest
- CreateFoodIntegrationTest
- AuthControllerTest -> 컨텍스트 새로 생성
- CustomHistoryRepositoryImpl -> 컨텍스트 새로 생성
- SchemaValidationTest -> 컨텍스트 새로 생성
- GetFoodIntegrationTest -> 컨텍스트 새로 생성
- UpdateWeightControllerTest -> 컨텍스트 새로 생성
- CreateFoodControllerTest -> 컨텍스트 새로 생성
- GetAchieveControllerTest -> 컨텍스트 새로 생성
- OnBoardingControllerTest -> 컨텍스트 새로 생성
- GetWeightIntegrationTest
- EatMealControllerTest -> 컨텍스트 새로 생성
- GetAchieveIntegarationTest
- EmitterControllerTest -> 컨텍스트 새로 생성
- CheckMemberEmailIntegrationTest
- UpdateMemerControllertest -> 컨텍스트 새로 생성
- EatMealIntegrationTest
- GetMealControllerTest -> 컨텍스트 새로 생성

 

목표 설정

 

따라서, 테스트를 통합테스트, 웹 계층 테스트, 영속성 계층 테스트로 분류하고, 성격이 같은 테스트 마다 같은 ApplicaitonContext를 사용하도록 만들어 ApplicaitonContext를 최대한 재활용하고자 하였습니다.

구체적인 실행 방법

 

 

위 사진과 같이 ControllerTest라는 추상 클래스를 사용하여 모든 웹 계층 테스트 시 사용하는 @MockBean을 한 곳으로 모아 ApplicationContext가 웹 계층 테스트시에는 한 번만 실행되게끔 만들었습니다.

 

 

@MockBean을 클래스위에 사용하면 ApplicationContext에 등록은 되지만, 해당 필드에 주입이 되지 않아 @Autowired를 사용하여 명시적으로 주입해주었습니다.

결과

 

 

 

Application Context를 재활용하지 못한 횟수가 13회에서 4회로 줄었습니다.
수행 시간 자체는 크게 줄지는 않았지만, 테스트 수가 더 많아지면 많아질 수록 더 유의미한 결과가 있을 것이라 생각됩니다.

 

 

해당 PR을 아래 링크에서 확인 가능합니다.

 

https://github.com/JNU-econovation/EATceed/pull/430

 

[Be/Test] 테스트 환경 개선 by hwangdaesun · Pull Request #430 · JNU-econovation/EATceed

📌 관련 이슈 #429 🔑 주요 변경사항 Spring TestContext Framework는 Spring ApplicationContext 인스턴스 및 WebApplicationContext 인스턴스의 일관된 로딩과 해당 컨텍스트의 캐싱을 제공합니다. 하지만, 현재 개발

github.com

 

728x90