자바 백엔드 입문 53편 — MockMvc로 컨트롤러 테스트

2026-05-16자바 백엔드 입문

자바 백엔드 입문 53편. Phase 7 마무리. MockMvc로 컨트롤러를 실제 Tomcat 없이 HTTP 요청·응답을 시뮬레이션하는 표준 패턴을 실전 코드로 풀어쓴 학습 노트.

📚 자바 백엔드 입문 · 53편 — MockMvc로 컨트롤러 테스트

이 글은 자바 백엔드 입문 시리즈 59편 중 53편이에요. Phase 7 Testing 마지막 글입니다. 52편에서 짧게 만난 MockMvc를 본격적으로 풀어 가요. Tomcat 없이 HTTP 요청·응답을 시뮬레이션하는 핵심 도구.

MockMvc — Tomcat 없이 HTTP 테스트

@SpringBootTest 통합 테스트로 실제 Tomcat을 띄울 수도 있지만 — 너무 무거워요. MockMvc"진짜 HTTP 서버 없이" DispatcherServlet 흐름을 메모리에서 시뮬레이션해줘요.

@WebMvcTest(OrderController.class)
class OrderControllerTest {

    @Autowired private MockMvc mockMvc;
    @MockBean private OrderService orderService;

    @Test
    void 주문_조회_성공() throws Exception {
        // Given
        when(orderService.findById(1L))
                .thenReturn(new Order(1L, 10000, "PENDING"));

        // When + Then
        mockMvc.perform(get("/api/orders/1"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.id").value(1))
                .andExpect(jsonPath("$.amount").value(10000))
                .andExpect(jsonPath("$.status").value("PENDING"));
    }
}

핵심 부품 — MockMvc + @WebMvcTest 조합. Controller 한 개만 띄우고 서비스는 @MockBean 으로 대체.

perform·andExpect·andDo 3단계

MockMvc 테스트의 표준 구조.

mockMvc.perform(요청)        // 1단계: HTTP 요청 흉내
        .andExpect(검증1)     // 2단계: 응답 검증
        .andExpect(검증2)
        .andDo(부가 동작);    // 3단계: 결과 출력·로깅

perform — 요청 빌더

// GET
mockMvc.perform(get("/api/orders/1"));

// POST + JSON 본문
mockMvc.perform(post("/api/orders")
        .contentType(MediaType.APPLICATION_JSON)
        .content("{\"productId\":1,\"amount\":10000}"));

// 쿼리 파라미터
mockMvc.perform(get("/api/orders")
        .param("status", "ACTIVE")
        .param("page", "1"));

// 헤더
mockMvc.perform(get("/api/orders/1")
        .header("Authorization", "Bearer token123"));

andExpect — 응답 검증

자주 만나는 검증 함수.

함수 용도
status().isOk() 200
status().isCreated() 201
status().isBadRequest() 400
status().isNotFound() 404
jsonPath("$.field").value(...) JSON 필드 값
jsonPath("$.list").isArray() 배열 여부
jsonPath("$.list.length()").value(3) 배열 크기
content().string("...") 응답 본문 전체
header().string("Location", "/orders/1") 응답 헤더

andDo — 부가 동작

.andDo(print())   // 요청·응답 콘솔 출력 (디버깅)
.andDo(log())     // 로거에 출력

테스트 실패 시 "실제로 어떤 응답이 왔는지" 보기 위해 print() 자주 박아요.

ObjectMapper로 JSON 본문 깔끔하게

문자열로 JSON 박으면 따옴표 escape가 번거로워요. Jackson ObjectMapper 활용.

@Autowired private ObjectMapper objectMapper;

@Test
void 주문_생성() throws Exception {
    OrderRequest request = new OrderRequest(1L, 10000);

    mockMvc.perform(post("/api/orders")
            .contentType(MediaType.APPLICATION_JSON)
            .content(objectMapper.writeValueAsString(request)))
            .andExpect(status().isCreated());
}

ObjectMapper도 @WebMvcTest 자동 주입. 별도 설정 X.

@Valid 검증 실패 테스트

33편 의 글로벌 예외 처리 + 34편 의 Bean Validation이 합쳐진 검증 테스트.

@Test
void 잘못된_요청_검증_실패() throws Exception {
    String invalidJson = "{\"productId\":null,\"amount\":-100}";

    mockMvc.perform(post("/api/orders")
            .contentType(MediaType.APPLICATION_JSON)
            .content(invalidJson))
            .andExpect(status().isBadRequest())
            .andExpect(jsonPath("$.code").value("VALIDATION_FAILED"))
            .andExpect(jsonPath("$.fieldErrors[*].field")
                    .value(containsInAnyOrder("productId", "amount")));
}

검증 실패 시 글로벌 핸들러가 400 + 오류 JSON을 만들어줘요. 그 응답 구조를 그대로 검증.

인증 테스트 — @WithMockUser

Spring Security가 박혀 있을 때, 인증된 사용자 시뮬레이션.

@Test
@WithMockUser(username = "alice", roles = {"USER"})
void 인증된_사용자_조회() throws Exception {
    mockMvc.perform(get("/api/orders/me"))
            .andExpect(status().isOk());
}

@Test
@WithMockUser(roles = {"USER"})
void 권한_없으면_403() throws Exception {
    mockMvc.perform(post("/api/admin/cleanup"))
            .andExpect(status().isForbidden());
}

@WithMockUser"이 테스트는 alice 사용자로 인증된 상태" 를 자동 설정. Spring Security 통합 테스트 표준 패턴.

MockMvcTester — 새로운 AssertJ 통합

Spring Framework 6.2부터 새 빌더 MockMvcTester 등장. AssertJ 스타일.

@Autowired private MockMvcTester mockMvcTester;

@Test
void 주문_조회() {
    assertThat(mockMvcTester.get().uri("/api/orders/1"))
            .hasStatusOk()
            .bodyJson()
            .extractingPath("$.amount").isEqualTo(10000);
}

기존 MockMvc보다 더 자연스러운 체이닝. 신규 프로젝트에서 시도해볼 만하지만 — 기존 MockMvc도 여전히 표준.

@SpringBootTest + MockMvc 통합 테스트

전체 흐름을 실제로 검증하려면.

@SpringBootTest
@AutoConfigureMockMvc
@Transactional
class OrderE2ETest {

    @Autowired private MockMvc mockMvc;
    @Autowired private OrderRepository orderRepo;
    @Autowired private ObjectMapper objectMapper;

    @Test
    void 주문_생성_API_전체_흐름() throws Exception {
        OrderRequest req = new OrderRequest(1L, 10000);

        mockMvc.perform(post("/api/orders")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(req)))
                .andExpect(status().isCreated())
                .andExpect(jsonPath("$.id").exists());

        // DB 실제 반영 확인
        assertThat(orderRepo.findAll()).hasSize(1);
    }
}

@SpringBootTest + @AutoConfigureMockMvc"전체 컨테이너 띄움 + MockMvc 활성화". 실제 Service·Repository·DB까지 다 거쳐 검증.

🎯 테스트 피라미드

건강한 테스트 구성 = 단위 테스트 80%(가장 가벼움, Mockito만) + 슬라이스 테스트 15%(@WebMvcTest·@DataJpaTest) + E2E 통합 테스트 5%(@SpringBootTest). 위로 갈수록 무겁고 느림 — 적절히 분배하는 게 핵심.

한 줄 정리 — MockMvc = Tomcat 없이 HTTP 요청 시뮬레이션. perform·andExpect·andDo 3단계 구조. @WebMvcTest 와 조합으로 가볍게, @SpringBootTest + @AutoConfigureMockMvc 로 전체 흐름 검증.

시험 직전 한 번 더 — MockMvc 입문자가 매번 헷갈리는 것

  • MockMvc = Tomcat 없이 HTTP 요청·응답 시뮬레이션
  • 표준 구조 = performandExpectandDo
  • 요청 빌더 = get·post·put·patch·delete
  • 본문 = .contentType(MediaType.APPLICATION_JSON).content("...")
  • ObjectMapper로 JSON 직렬화 자동
  • 검증 = status().isOk()·jsonPath("$.field")
  • jsonPath = JSON 경로 표현식 ($ = 루트)
  • andDo(print()) = 디버깅용 응답 출력
  • @WebMvcTest(컨트롤러.class) = Controller만 띄움 (빠름)
  • @MockBean = Service·Repository 가짜 객체로
  • @WithMockUser = Spring Security 인증 시뮬레이션
  • @SpringBootTest + @AutoConfigureMockMvc = 전체 통합 + MockMvc
  • 검증 실패 응답 = status().isBadRequest() + jsonPath("$.fieldErrors")
  • MockMvcTester = Spring Framework 6.2 신규 AssertJ 스타일
  • 기존 MockMvc도 여전히 표준
  • 테스트 피라미드 = 단위 80% + 슬라이스 15% + E2E 5%
  • 가벼운 테스트가 빠르고 안정 — 무거운 테스트는 적게
  • @Transactional + 자동 롤백으로 DB 격리
  • MockMvc는 정적 import 표준 (MockMvcRequestBuilders.*·MockMvcResultMatchers.*)
  • IntelliJ에서 자동 import 처리됨

시리즈 다른 편 (앞뒤 글 모음)

이전 글:

다음 글:

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

답글 남기기

error: Content is protected !!