TIL - 20251128

juni·2025년 11월 28일

TIL

목록 보기
191/317

1128 Spring Boot 테스트 심화: Controller 테스트와 MockMvc


✅ 1. 컨트롤러 테스트의 특징과 @WebMvcTest

  • 컨트롤러(Controller)는 웹 계층의 일부로, HTTP 요청을 받고 응답을 보내는 역할을 합니다. 따라서 컨트롤러 테스트는 단순히 메서드를 호출하는 것을 넘어, 실제 HTTP 요청과 응답을 시뮬레이션하는 방식으로 이루어져야 합니다.

  • 문제점: @SpringBootTest를 사용하면 컨트롤러 테스트는 가능하지만, 서비스, 리포지토리 등 모든 Bean을 로드하므로 너무 무겁고 느립니다. 컨트롤러의 역할(요청/응답 처리, 데이터 바인딩, 유효성 검증 등)만 독립적으로 테스트하고 싶습니다.

  • @WebMvcTest:

    • 웹 계층만을 테스트하기 위한 슬라이스 테스트(Slice Test) 어노테이션입니다.
    • @Controller, @RestController, @RestControllerAdvice, Filter, WebMvcConfigurer웹과 관련된 Bean들만 컨테이너에 로드합니다.
    • @Service, @Repository와 같은 비즈니스 로직 관련 Bean들은 로드하지 않습니다. 따라서 이들의 의존성은 반드시 @MockBean을 사용하여 가짜(Mock) 객체로 대체해야 합니다.

✅ 2. MockMvc: 가짜 HTTP 요청 생성기

  • MockMvc는 Spring MVC 테스트 프레임워크의 핵심으로, 실제 서블릿 컨테이너(Tomcat)를 실행하지 않고도, 스프링 MVC 동작을 모의(mocking)하여 컨트롤러에 대한 HTTP 요청을 시뮬레이션할 수 있게 해주는 도구입니다.

  • 동작 방식: DispatcherServlet과 유사한 테스트용 모형을 만들어, 마치 브라우저나 API 클라이언트가 요청을 보낸 것처럼 컨트롤러를 테스트할 수 있습니다.

➕ MockMvc의 주요 메서드 체인

  1. perform(requestBuilder): HTTP 요청을 실행합니다.

    • get("/api/users"), post("/api/users")MockMvcRequestBuilders를 사용하여 요청을 정의합니다.
    • .contentType(): 요청의 Content-Type 헤더를 설정합니다. (e.g., MediaType.APPLICATION_JSON)
    • .content(): 요청의 본문(Body)에 담을 데이터를 설정합니다. (JSON 문자열 등)
  2. andExpect(resultMatcher): 응답 결과를 검증합니다.

    • status().isOk(): HTTP 상태 코드가 200 OK인지 검증.
    • status().isCreated(): 상태 코드가 201 Created인지 검증.
    • jsonPath("$.fieldName").value(expectedValue): JSON 응답의 특정 필드 값이 기대값과 일치하는지 검증.
    • content().string(): 응답 본문 전체가 특정 문자열과 일치하는지 검증.
  3. andDo(resultHandler): 요청/응답의 전체 내용을 출력하는 등 추가적인 작업을 수행합니다.

    • print(): 요청과 응답의 모든 내용을 콘솔에 상세하게 출력하여 디버깅에 매우 유용합니다.

✅ 3. @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을 사용하여 가짜 객체로 대체해야 합니다.
  • MockMvc는 실제 서버를 띄우지 않고도, HTTP 요청을 시뮬레이션하고 응답 결과를 상세하게 검증할 수 있게 해주는 강력한 테스트 도구입니다.
  • perform()으로 요청을 보내고, andExpect()로 결과를 검증하며, andDo(print())로 과정을 디버깅하는 것이 MockMvc 테스트의 일반적인 흐름입니다.

0개의 댓글