@WebMvcTest컨트롤러(Controller)는 웹 계층의 일부로, HTTP 요청을 받고 응답을 보내는 역할을 합니다. 따라서 컨트롤러 테스트는 단순히 메서드를 호출하는 것을 넘어, 실제 HTTP 요청과 응답을 시뮬레이션하는 방식으로 이루어져야 합니다.
문제점: @SpringBootTest를 사용하면 컨트롤러 테스트는 가능하지만, 서비스, 리포지토리 등 모든 Bean을 로드하므로 너무 무겁고 느립니다. 컨트롤러의 역할(요청/응답 처리, 데이터 바인딩, 유효성 검증 등)만 독립적으로 테스트하고 싶습니다.
@WebMvcTest:
@Controller, @RestController, @RestControllerAdvice, Filter, WebMvcConfigurer 등 웹과 관련된 Bean들만 컨테이너에 로드합니다.@Service, @Repository와 같은 비즈니스 로직 관련 Bean들은 로드하지 않습니다. 따라서 이들의 의존성은 반드시 @MockBean을 사용하여 가짜(Mock) 객체로 대체해야 합니다.MockMvc는 Spring MVC 테스트 프레임워크의 핵심으로, 실제 서블릿 컨테이너(Tomcat)를 실행하지 않고도, 스프링 MVC 동작을 모의(mocking)하여 컨트롤러에 대한 HTTP 요청을 시뮬레이션할 수 있게 해주는 도구입니다.
동작 방식: DispatcherServlet과 유사한 테스트용 모형을 만들어, 마치 브라우저나 API 클라이언트가 요청을 보낸 것처럼 컨트롤러를 테스트할 수 있습니다.
perform(requestBuilder): HTTP 요청을 실행합니다.
get("/api/users"), post("/api/users") 등 MockMvcRequestBuilders를 사용하여 요청을 정의합니다..contentType(): 요청의 Content-Type 헤더를 설정합니다. (e.g., MediaType.APPLICATION_JSON).content(): 요청의 본문(Body)에 담을 데이터를 설정합니다. (JSON 문자열 등)andExpect(resultMatcher): 응답 결과를 검증합니다.
status().isOk(): HTTP 상태 코드가 200 OK인지 검증.status().isCreated(): 상태 코드가 201 Created인지 검증.jsonPath("$.fieldName").value(expectedValue): JSON 응답의 특정 필드 값이 기대값과 일치하는지 검증.content().string(): 응답 본문 전체가 특정 문자열과 일치하는지 검증.andDo(resultHandler): 요청/응답의 전체 내용을 출력하는 등 추가적인 작업을 수행합니다.
print(): 요청과 응답의 모든 내용을 콘솔에 상세하게 출력하여 디버깅에 매우 유용합니다.@WebMvcTest를 이용한 컨트롤러 테스트 예시@MockBean을 사용하여 서비스를 가짜 객체로 만들고, 그 행동을 given().willReturn() (Mockito의 when().thenReturn()과 동일)으로 정의하는 것이 핵심입니다.@WebMvcTest(UserController.class) // UserController와 관련된 웹 계층만 테스트
class UserControllerTest {
@Autowired
private MockMvc mockMvc; // HTTP 요청을 시뮬레이션
@Autowired
private ObjectMapper objectMapper; // Java 객체를 JSON 문자열로 변환하기 위해 주입
@MockBean // UserController가 의존하는 UserService를 가짜 Bean으로 등록
private UserService userService;
@Test
@DisplayName("회원가입 성공 API 테스트")
void signUp_Success() throws Exception {
// given - 테스트 준비
SignUpRequest request = new SignUpRequest("test@example.com", "password123");
User responseUser = new User(1L, "test@example.com", "encodedPassword");
// userService.signUp() 메서드가 호출되면, responseUser를 반환하도록 정의
given(userService.signUp(any(SignUpRequest.class))).willReturn(responseUser);
// when & then - 테스트 실행 및 검증
mockMvc.perform(post("/api/users/signup") // POST /api/users/signup 요청
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request))) // 요청 본문에 JSON 데이터 포함
.andExpect(status().isCreated()) // HTTP 상태 코드가 201 Created인지 확인
.andExpect(jsonPath("$.id").value(1L)) // 응답 JSON의 id 필드 값 확인
.andExpect(jsonPath("$.email").value("test@example.com"))
.andDo(print()); // 요청/응답 내용 출력
}
@Test
@DisplayName("회원가입 실패 - 유효성 검증 실패 (이메일 형식 오류)")
void signUp_Fail_Validation() throws Exception {
// given
SignUpRequest request = new SignUpRequest("invalid-email", "password123"); // 잘못된 이메일 형식
// when & then
mockMvc.perform(post("/api/users/signup")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isBadRequest()) // @Valid에 의해 400 Bad Request가 발생하는지 확인
.andDo(print());
}
}
@WebMvcTest는 웹 계층만 가볍게 테스트할 수 있는 슬라이스 테스트 어노테이션이며, 테스트 대상이 아닌 서비스나 리포지토리 계층은 @MockBean을 사용하여 가짜 객체로 대체해야 합니다.perform()으로 요청을 보내고, andExpect()로 결과를 검증하며, andDo(print())로 과정을 디버깅하는 것이 MockMvc 테스트의 일반적인 흐름입니다.