본문 바로가기
Java

[Java] @WebMvcTest 테스트 시간 20% 줄이기

by slf4j 2026. 1. 29.
반응형

발단

회사에서 컨트롤러 테스트가 60개쯤 있다. ./gradlew test 를 돌리면 평균 9분 44초가 걸렸다. 

테스트 로그를 보다가 이상한 점을 발견했다.

Initializing Spring TestDispatcherServlet ''
Initializing Spring TestDispatcherServlet ''
Initializing Spring TestDispatcherServlet ''
...

 

이게 60번 넘게 반복되고 있었다. 뭔가 이상하다.

 

원인

@WebMvcTest는 슬라이스 테스트라서 빠르다고 알고 있었다. 근데 왜 이렇게 느릴까?

@WebMvcTest(MemberRestController.class)
class MemberRestControllerTest extends WebMvcTestBase { }

@WebMvcTest(OrderRestController.class)
class OrderRestControllerTest extends WebMvcTestBase { }

 

 

문제는 각 테스트 클래스가 다른 컨트롤러를 지정한다는 거다. Spring은 이걸 다른 설정으로 인식해서 매번 새로운 ApplicationContext를 만든다.

 

60개 테스트 = 60개 context = 60번의 Spring 부팅

그래서 느렸던 거다...

 

 

해결

webAppContextSetup 대신 standaloneSetup 을 쓰기로 했다.

 

기존 코드:

@BeforeEach
void setUp() {
    this.mockMvc = MockMvcBuilders.webAppContextSetup(wac)
            .alwaysDo(print())
            .build();
}

 

변경 후:

@BeforeEach
void setUp() {
    Object controller = findController();
    MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
    converter.setObjectMapper(objectMapper);

    this.mockMvc = MockMvcBuilders.standaloneSetup(controller)
            .setControllerAdvice(new BusinessExceptionAdvice(), new CommonExceptionAdvice())
            .setMessageConverters(converter)
            .alwaysDo(print())
            .build();
}

private Object findController() {
    WebMvcTest annotation = this.getClass().getAnnotation(WebMvcTest.class);
    if (annotation != null && annotation.value().length > 0) {
        Class<?> controllerClass = annotation.value()[0];
        return applicationContext.getBean(controllerClass);
    }
    throw new IllegalStateException("@WebMvcTest annotation not found");
}

 

60개 테스트 클래스를 하나하나 수정하기 싫어서 Reflection으로 @WebMvcTest 어노테이션에서 컨트롤러를 자동으로 꺼내오게 했다.

 

 

삽질

1. 400이 안 나온다

 

입력값 검증 테스트에서 400이 나와야 하는데 200이 나왔다. standaloneSetup은 @ControllerAdvice를 자동으로 등록 안 해준다... setControllerAdvice()로 직접 넣어줘야 한다.

 

2. 날짜가 배열로 나온다

"saleDate": [2025, 9, 1]

 

"2025-09-01"이 나와야 하는데 배열로 나왔다. standaloneSetup은 Spring Boot의 ObjectMapper 설정을 안 쓴다. MappingJackson2HttpMessageConverter에 직접 넣어줘야 한다.

 

3. RUNTIME_EXCEPTION 에러 코드

 

MemberException이 발생하면 적절한 에러 코드가 나와야 하는데 RUNTIME_EXCEPTION으로 나왔다. 우리 프로젝트는 예외 구조가 이렇다:

RuntimeException
ㄴ BusinessException  ← MemberException이 여기 상속
    ㄴ CommonException

 

CommonExceptionAdvice만 등록했더니 BusinessException을 못 잡았다. BusinessExceptionAdvice도 추가해야 했다.

 

 

결과

테스트 회차 Before After
1 9:09 6:51
2 9:59 8:27
3 1-:05 8:39

 

평균 9분 44초에서 7분 59초로, 약 2분(18~25%) 단축됐다.

 

주의할 점

  • standaloneSetup은 Security 필터가 없다. 인증/인가 테스트는 다른 방법을 써야 한다.
  • @ControllerAdvice 자동 등록 안 된다.
  • Spring Boot의 ObjectMapper 설정 자동 적용 안 된다.
  • 통합테스트 코드에는 적용하면 안 된다.

입력값 검증이나 HTTP 상태 코드 검증 같은 단순한 컨트롤러 테스트에는 standaloneSetup이 좋은 것 같다...

 

매일 테스트 돌릴 때마다 2분씩 아끼면 1년이면 꽤 된다.

반응형