개발 일지

Spring/JUnit

테스트 환경 통합하기 (@SpringBootTest 남용 피하기)

junjun_ 2024. 5. 3. 16:53

이번 글에서는 테스트 환경을 통합하여 테스트 시간을 단축하는 내용을 작성해 보려고 합니다.

 

흔히 통합 테스트 코드를 작성할 때 각 테스트 클래스마다 `@SpringBootTest` 어노테이션을 사용합니다.

그러나 이런 방식은 각 테스트 클래스가 독립적으로 애플리케이션 컨텍스트를 생성하게 되어 테스트 실행 시마다 서버를 다시 시작하고 컨텍스트를 로딩해야 합니다.  이로 인해 테스트 실행 시간이 길어지고 리소스가 낭비되는 문제가 발생할 수 있습니다.

 


해결 방안

@SpringBootTest의 남용을 피하는 방법은 간단합니다. 각 테스트 클래스마다 @SpringBootTest를 사용하는 대신,

@SpringBootTest 어노테이션을 붙인 추상 클래스(abstract)를 생성하고 다른 클래스에서 이를 상속받아 애플리케이션 컨텍스트의 재사용하게 만드는 것입니다.

  • @SpringBootTest 어노테이션을 붙인 추상 클래스를 만들기
  • 이를 다른 테스트 클래스에서 상속받아 사용하기
  • 테스트 환경을 통합하여 애플리케이션 컨텍스트의 재사용을 극대화

적용하기

 

1. @SpringBootTest 어노테이션을 붙인 abstract 클래스를 작성합니다.

  • 공통으로 사용하는 bean이나 설정이 있다면 같이 작성합니다.

 

IntegrationTestSupport.java

@ActiveProfiles("test")
@SpringBootTest
public abstract class IntegrationTestSupport {

	 // 공통 설정 및 Bean 정의
    @MockBean
    protected MailSendClient mailSendClient;
}

 

 

 

2. @SpringBootTest 어노테이션 대신 IntegrationTestSupport 클래스를 상속받게 합니다.

 

OrderServiceTest.java (@SpringBootTest)

@SpringBootTest
class OrderServiceTest {

}

 

OrderServiceTest.java (extend IntegrationTestSupport class)

class OrderServiceTest extends IntegrationTestSupport {

}

 

이렇게 작성하면 간단하게 @SpringBootTest 어노테이션이 붙은 테스트의 환경을 통합할 수 있습니다. 

 

여기서 끝이 아니라 한가지 더 해주어야 하는데 컨트롤러 테스트의 경우 mocking을 사용해서 @SpringBootTest 가 아닌 `@WebMvcTest` 어노테이션을 사용했습니다.  @WebMvcTest 또한 각각의 Spring Boot 애플리케이션 컨텍스트를 생성하기 때문에  @WebMvcTest을 붙인  컨트롤러용 abstract 클래스를 따로 작성해 주어야 합니다. 구조는 위와 같음으로 코드만 적어 두겠습니다.

 

ControllerTestSupport.java

@WebMvcTest(controllers = {
    OrderController.class,
    ProductController.class
})
public abstract class ControllerTestSupport {
    @Autowired
    protected MockMvc mockMvc;

    @Autowired
    protected ObjectMapper objectMapper;

    @MockBean
    protected OrderService orderService;

    @MockBean
    protected ProductService productService;
}

 

OrderControllerTest.java

class OrderControllerTest extends ControllerTestSupport {

}

 

 

결과 확인하기

테스트 환경을 통합하여 abstract  클래스를 상속받는 구조로 변경 완료했습니다. 이제 실행시간이 얼마나 차이가 있는지 확인해 보겠습니다.

 

before

  • Springboot 서버가 7번 띄워졌고 4.519초

 

after

  • Springboot 서버가 2번 띄워졌고 3.162초

테스트 코드가 44개 뿐인 작은 프로젝트임에도 불구하고 유의미한 시간 단축을 확인할 수 있었습니다.

정리

각각의 테스트 클래스에  @SpringBootTest, @WebMvcTest를 사용하면 테스트마다 독립적인 Spring Boot 애플리케이션 컨텍스트가 생성됩니다. 이는 테스트하는 데 걸리는 시간을 증가시키고 시간은 비용이기 때문에 결과적으로 비용을 증가시킵니다. 이를 막기 위하여 추상클래스에 어노테이션을 사용하고 각 클래스에서 이를 상속받는 방법으로 테스트 환경을 통합을 시켰고 작은 토이 프로젝트임에도 불구하고 유의미한 시간 단축의 결과를 확인할 수 있었습니다.

 

앞으로 규모가 큰 프로젝트를 경험하게 될 때에도 이러한 리소스들을 어떻게 관리하는지가 중요할 거 같아서 남용되고 있는 자원이 어떠한 것들이 있는지 관심 있게 살펴보아야겠습니다. 또한 @SpringBootTest, @WebMvcTest 들이 애플리케이션 컨텍스트를 생성하는 과정에 대해서도 정리해 보려고 합니다.