이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
MockMvc는 실제 서버 없이 어떤 MVC 흐름을 검증하는가?@WebMvcTest는 어떤 Bean을 로드하고 어떤 Bean은 테스트 더블로 대체해야 하는가?- Validation, ExceptionHandler, Security가 Controller 테스트에 들어올 때 무엇을 명시해야 하는가?
개요
Controller 테스트는 HTTP 계약을 검증한다. URL, method, request body, validation, response status, JSON body, header, exception advice가 의도대로 동작하는지 확인한다. 이때 MockMvc와 @WebMvcTest가 자주 쓰인다.
기존 MVC 테스트 문서에서 다룬 MockMvc, RequestBuilder, perform, ResultActions, ResultMatcher, jsonPath 흐름은 이 문서로 흡수한다. 핵심은 실제 서버를 띄우지 않고 DispatcherServlet 기반 MVC 흐름을 실행한다는 점이다.
MockMvc 흐름
MockMvc는 mock Servlet 환경에서 요청을 만든다. perform은 DispatcherServlet을 통해 요청을 실행하고, 결과는 andExpect로 검증한다.
@WebMvcTest(MemberController.class)
class MemberControllerTest {
@Autowired MockMvc mockMvc;
@MockitoBean MemberService memberService;
@Test
void createMember() throws Exception {
given(memberService.create(any())).willReturn(new MemberResponse(1L, "a@b.com"));
mockMvc.perform(post("/api/members")
.contentType(MediaType.APPLICATION_JSON)
.content("""
{"email":"a@b.com","password":"secret123"}
"""))
.andExpect(status().isCreated())
.andExpect(header().string("Location", "/api/members/1"))
.andExpect(jsonPath("$.email").value("a@b.com"));
}
}현재 Spring Framework와 Spring Boot 테스트 문서에서는 Mockito 기반 Bean override에 @MockitoBean이 등장한다. 사용하는 Spring Boot 버전에 따라 @MockBean을 쓰는 프로젝트도 있으므로 프로젝트 버전의 테스트 문서를 함께 확인해야 한다.
WebMvcTest 범위
@WebMvcTest는 MVC 계층에 집중한다. Controller, ControllerAdvice, JSON serialization, Validation, MVC config 일부가 테스트 대상이다. Service, Repository, 외부 client는 기본적으로 로드하지 않는다.
그래서 Controller가 의존하는 Service는 mock이나 fake로 대체한다. 이 테스트에서 검증할 것은 Service 비즈니스 로직이 아니라 요청이 올바르게 파싱되고, 검증되고, Service로 위임되고, 응답으로 변환되는 HTTP 계약이다.
전체 Spring Boot context는 필요하지만 실제 서버는 띄우고 싶지 않다면 @SpringBootTest와 @AutoConfigureMockMvc 조합을 사용한다. 반대로 실제 port, network, container-specific Servlet 동작까지 보려면 RANDOM_PORT 기반 통합 테스트가 더 적합하다.
Validation과 ExceptionHandler
검증 실패 테스트는 매우 중요하다. 정상 요청만 테스트하면 @Valid, BindingResult, 공통 예외 응답 형식이 깨져도 놓칠 수 있다.
mockMvc.perform(post("/api/members")
.contentType(MediaType.APPLICATION_JSON)
.content("""
{"email":"","password":"123"}
"""))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.code").value("VALIDATION_FAILED"));공통 @RestControllerAdvice가 WebMvcTest에 포함되는지 확인한다. 필요한 Advice만 import해야 하는 경우도 있다.
Security 테스트
Security가 켜진 Controller 테스트에서는 인증 상태를 명시해야 한다. 보호 API에 인증 없이 요청하면 Controller 로직이 아니라 Security FilterChain에서 401 또는 403이 날 수 있다.
CSRF가 필요한 POST, PUT, DELETE 요청이면 테스트에서도 CSRF token을 붙여야 한다. JWT나 role 기반 권한 테스트라면 Spring Security test 지원을 이용해 사용자, authority, token 상태를 명확히 설정한다.
실전 팁
- Controller 테스트는 정상 응답, 검증 실패, 예외 변환, 권한 실패를 함께 둔다.
- JSON 응답은
jsonPath로 외부 계약을 고정한다. - Service mock은 필요한 응답만 최소 설정한다.
- Security가 관여하는 테스트는 인증 사용자와 CSRF 의도를 명시한다.
- 전체 context가 필요한 Controller 테스트는
@SpringBootTest와@AutoConfigureMockMvc를 고려한다. - WebMvcTest에서 실패가 나면 먼저 MVC slice에 필요한 Bean이 포함되어 있는지 확인한다.
위험 신호!
- Controller 테스트가 Service 내부 분기까지 mock 호출 순서로 검증한다.
- 검증 실패와 예외 응답 테스트가 없다.
- Security 때문에 나는 401/403을 Controller bug로 해석한다.
- WebMvcTest에 필요한 Advice나 Converter가 빠져 실제 API 응답과 다르다.
- MockMvc로 실제 servlet container의 저수준 동작까지 검증했다고 생각한다.
- 모든 Controller 테스트를
@SpringBootTest와 실제 서버로만 수행한다.
확인 질문
확인 질문
- MockMvc의
perform은 무엇을 실행하는가?
- mock Servlet 환경에서 DispatcherServlet 요청 처리 흐름을 실행한다.
- WebMvcTest의 관심사는 무엇인가?
- Controller의 HTTP 요청/응답 계약과 MVC 계층 동작이다.
- 보호 API Controller 테스트에서 403이 날 때 확인할 것은 무엇인가?
- 인증 사용자, authority, CSRF token, Security filter 적용 여부를 확인해야 한다.