장바구니 미션을 진행하면서 작성한 테스트 중에서 애플리캐이션 컨텍스트를 이용하는 테스트를 3가지 작성했다.
Controller를 테스트하는 @WebMvcTest, Dao를 테스트하는 @JdbcTest, RestAssured를 이용하여 E2E 테스트를 진행하는 @SpringBootTest를 만들었다.

테스트를 돌려보면 intellij에 측정되는 테스트 수행 시간은 6초로 매우 짧다.



하지만 이는 컨텍스트 로딩을 제외한 시간이며 실제로는 6초보다 훨씬 많이 걸린다.



profiler를 통해 측정한 테스트 시간에서 컨텍스트 로딩을 포함한 시간은 43초나 된다. 
로직을 수행하는 데 6초 정도밖에 걸리지 않지만 애플리케이션 컨텍스트를 로딩하는데 너무 많은 시간을 소요하고 있다. 
당장은 기다릴만 하지만 애플리케이션이 커지고 테스트도 많아지면 이를 감당하기 힘들 것이며 변경이 반영될 때마다 테스트 코드를 실행하기 두려워질 것이다.

스프링 공식문서에서는 테스트가 같은 설정을 공유한다면 시간이 많이 소요되는 컨텍스트 로딩이 한 번만 발생한다고 설명하고 있다.

 

Spring’s test framework caches application contexts between tests. Therefore, as long as your tests share the same configuration (no matter how it is discovered), the potentially time-consuming process of loading the context happens only once.

 

즉, 컨텍스트 설정을 같게 만들어주면 캐싱된 컨텍스트를 계속 재사용하여 컨텍스트 로딩 횟수를 줄이고, 테스트 작동 시간을 줄일 수 있다.

코드를 확인하며 컨텍스트 설정인 빈을 설정하는 부분을 설정 파일로 분리하고 이를 공유한다면 같은 컨텍스트를 사용할 수 있을 것이다.

@WebMvcTest(CartItemsController.class)  
class CartItemsControllerTest {  
    @MockBean  
    private CartService cartService;  
    @MockBean  
    AuthenticationPrincipalArgumentResolver authenticationPrincipalArgumentResolver;  
    @MockBean  
    BasicAuthInterceptor basicAuthInterceptor;  
    ...
}


위 CartItemsControllerTest 에서는 테스트할 컨트롤러와 여러 의존성을 @MockBean으로 선언하여 컨텍스트를 설정하고 있다. 

@WebMvcTest(ProductsController.class)  
class ProductsControllerTest {  
	@MockBean  
	private ProductService productService;  
	@MockBean  
	AuthenticationPrincipalArgumentResolver authenticationPrincipalArgumentResolver;  
	@MockBean  
	BasicAuthInterceptor basicAuthInterceptor;
	...
}


ProductsController 또한 @MockBean을 통해 컨텍스트 설정을 하고 있다.

이렇게 컨텍스트 설정이 달라서 컨텍스트를 새로 만드는데 시간이 많이 소요되고 있었다.

이러한 빈 설정을 설정 파일에서 모아서 한꺼번에 하고 이 설정 파일을 로드한다면 같은 컨텍스트를 공유할 수 있을 것이다.

다음과 같이 컨트롤러와 관련된 빈은 MvcConfig 에, 인증과 관련된 빈은 AuthorizationConfig 에 선언을 했다.

@ComponentScan(basePackages = "cart.controller")  
@TestConfiguration  
public class MvcConfig {  
	@MockBean  
	CartService cartService;  
	@MockBean  
	ProductService productService;
	...
}
@TestConfiguration  
public class AuthorizationConfig {  
	@MockBean  
	AuthenticationPrincipalArgumentResolver authenticationPrincipalArgumentResolver;  
	@MockBean  
	BasicAuthInterceptor basicAuthInterceptor;  
	@MockBean  
	MemberService memberService;  
}


이 설정들을 @ContextConfiguration 을 통해 로드하고 @MockBean으로 설정했던 빈들을 @Autowired를 통해 주입받아서 사용하면 된다.

@ContextConfiguration(classes = {AuthorizationConfig.class, MvcConfig.class})  
@WebMvcTest  
class CartItemsControllerTest {  
	@Autowired  
	private CartService cartService;  
	@Autowired  
	AuthenticationPrincipalArgumentResolver authenticationPrincipalArgumentResolver;  
	@Autowired  
	BasicAuthInterceptor basicAuthInterceptor;
	...
}



이러한 방식으로 @WebMvcTest를 사용하는 테스트들끼리 같은 설정 파일을 만들어 공유하도록 하고, @JdbcTest도 설정 파일을 공유하도록 하여 (@SpringBootTest는 원래 같은 설정으로 구현했음) 컨텍스트 생성을 총 3번으로 줄이도록 개선했다.

이에 따른 성능 개선 결과는 다음과 같다.


우측의 테스트의 로직을 수행하는 시간은 5.9초로 비슷하지만 좌측의 profiler로 컨텍스트 로딩 시간까지 포함한 시간은 23초로 처음 43초에 비해서 크게 개선된 것을 확인할 수 있다.

참고 자료

https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#features.testing

https://bperhaps.tistory.com/entry/%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BD%94%EB%93%9C-%EC%B5%9C%EC%A0%81%ED%99%94-%EC%97%AC%ED%96%89%EA%B8%B0-1

+ Recent posts