본문 바로가기

Mockmvc로 통합 테스트 시 예외 감지

@정소민fan2025. 12. 4. 21:06

한창 mockmvc로 컨트롤러에 API 요청을 날려 시나리오를 테스트하는 통합 테스트를 작성하는 중이었다.

늘 하던대로 예외를 잡는 코드를 assertJ로 작성했다.

//when & then
assertThatThrownBy { makeReservation(request, userB_AccessToken, userB_WaitingToken) }
    .hasCauseInstanceOf(DuplicateResourceException::class.java)

그런데 예외가 잡히기는 하는데, 엉뚱한 SertvleException이 잡히는 것이 문제였다.

Expecting actual throwable to be an instance of:
kr.hhplus.be.server.exception.DuplicateResourceException
but was:
  jakarta.servlet.ServletException: Request processing failed: kr.hhplus.be.server.exception.DuplicateResourceException: 이미 예약되어있는 좌석입니다.
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1022)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:590)

원인이 무엇인지 찾아보니.. mockmvc를 사용할 때 발생하는 전형적인 스택 트레이스라는 것이다.

 

저 makeReservation은 유저의 액세스 토큰과 대기열 토큰을 받아 API를 호출하는 메소드이다.

private fun makeReservation(request: ReservationMakeRequest, access: String, waiting: String): ReservationResponse {
    val reservation = mockMvc.perform(
        post("/api/reservation") // 예약 생성
            .header("Authorization", "Bearer $access")
            .header("X-Waiting-Token", "Bearer $waiting")
            .contentType(MediaType.APPLICATION_JSON)
            .content(objectMapper.writeValueAsString(request))
    ).andExpect(status().isOk)
        .andExpect(jsonPath("$.date").value(request.date.toString()))
        .andExpect(jsonPath("$.seatNumber").value(request.seatNumber.toString()))
        .andReturn()
}

 

 

나는 당연히 서비스 내부에서 발생한 예외도 그대로 바깥으로 나올 줄 알았지만, 그건 아니었다.

 

스프링의 흐름을 보면 요청이 서블릿 필터를 지나고 디스패처 서블릿에서 적절한 핸들러 (컨트롤러/서비스)를 찾아 요청을 전달해주고, 응답을 받을 시에는 역순으로 받는다.

이 과정에서 핸들러에서 발생한 예외는 디스패처 서블릿에서 ServletException으로 래핑하여 밖으로 던진다.

위 함수에서 봤듯이, 서비스의 메소드를 직접 호출한게 아니라 외부에서 요청했기 때문에 발생한 예외가 래핑되어서 나타났고, 기대한던 예외와 다르다는 로그가 발생한 것이다.

 

그래서 보통은 @ControllerAdvice를 사용해서 디스패처 서블릿으로 전달되는 예외를 가로채어 적절한 HTTP 상태 코드를 입힌 ResponseEntity를 반환한다.

즉 그말은 무엇이냐? 이 프로젝트에서 내가 @ControllerAdviceDuplicateResourceException을 핸들링하는 메소드를 작성하지 않았다는 것이다...0_0

 

일단 추가해주자...

@ExceptionHandler
fun exceptionhandler(e: DuplicateResourceException): ResponseEntity<*> {
    log.error { "예외 발생 ${e.message}" }
    return errorResponse(HttpStatus.CONFLICT, e.message.toString())
}

 

그러면 테스트를 어떻게 해야할까? 저 예외가 발생하면 HTTP 상태 코드로 CONFLICT 코드가 전달되도록 하였으니

//when & then
mockMvc.perform(
    post("/api/reservation")
        .header("Authorization", "Bearer $userB_AccessToken")
        .header("X-Waiting-Token", "Bearer $userB_WaitingToken")
        .contentType(MediaType.APPLICATION_JSON)
        .content(objectMapper.writeValueAsString(request))
).andExpect(status().isConflict)

이렇게 기대되는 상태 코드를 CONFLICT로 넣으면....

 

오케이 통과

'Spring > Kotlin' 카테고리의 다른 글

Spring AI + Bedrock 사용해보기  (1) 2026.01.16
마이그레이션 계획  (0) 2025.12.03
서비스, API 작성  (2) 2025.10.01
리포지토리 만들기  (0) 2025.09.13
도메인 엔티티 작성  (0) 2025.09.01
정소민fan
@정소민fan :: 코딩은 관성이야

코딩은 관성적으로 해야합니다 즐거운 코딩 되세요

목차