Spring Boot에서 MockMvc를 활용한 컨트롤러 API 테스트

Spring Boot 애플리케이션을 개발할 때, 컨트롤러 계층의 동작을 검증하기 위해 전체 애플리케이션을 실행하지 않고도 HTTP 요청을 시뮬레이션할 수 있는 방법이 필요하다. 이를 가능하게 하는 핵심 도구가 바로 MockMvc이다. 이 기능은 Spring Test 프레임워크에 포함되어 있으며, 서버를 실제로 구동하지 않아도 웹 레이어의 단위 테스트를 수행할 수 있도록 지원한다.

MockMvc란?

MockMvc는 Spring MVC의 요청 처리 흐름을 모의(mock)하여 컨트롤러 메서드를 직접 호출하고 그 결과를 검증하는 데 사용된다. 외부 네트워크나 서버 실행 없이 HTTP 요청과 응답을 시뮬레이션할 수 있어, 테스트 속도가 빠르고 반복성이 보장된다. 또한, 다양한 매처(matcher)와 결과 핸들러를 제공해 응답 상태, 콘텐츠 타입, JSON 구조 등을 쉽게 검증할 수 있다.

테스트 대상 컨트롤러 예제

다음은 학생 정보를 조회하고 파일을 업로드하는 기능을 제공하는 REST 컨트롤러이다.

package com.example.testapp;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/students")
public class StudentApiController {

    @Autowired
    private StudentManagementService managementService;

    @GetMapping("/list")
    public ResponseEntity<List<Student>> retrieveAllStudents() {
        List<Student> students = managementService.loadAll();
        Student dummy = new Student();
        dummy.setName("anonymous");
        students.add(dummy);
        return ResponseEntity.ok(students);
    }

    @PostMapping("/import")
    public ResponseEntity<Void> importData(@RequestBody String payload) {
        managementService.processImport(payload);
        return ResponseEntity.accepted().build();
    }
}
    

필요한 의존성 설정

MockMvc를 사용하려면 spring-boot-starter-test를 포함해야 한다. 이 스타터는 JUnit, Mockito, Spring Test, JsonPath 등을 통합 제공하며, 특히 @AutoConfigureMockMvc를 통해 자동으로 MockMvc 인스턴스를 구성할 수 있게 해준다.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
    </exclusions>
</dependency>
    

주의할 점은, 만약 spring-boot-starter-security가 포함되어 있으면, 인증 없이는 엔드포인트 접근이 거부되어 테스트가 실패할 수 있으므로, 테스트 환경에서는 보안 설정을 비활성화하거나 별도의 테스트 프로필을 사용해야 한다.

컨트롤러 테스트 작성

다음은 위 컨트롤러를 테스트하는 JUnit 5 기반의 테스트 클래스이다. 서비스 계층은 Mockito를 사용해 목킹하고, MockMvc를 주입받아 요청을 시뮬레이션한다.

package com.example.testapp;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;

import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@WebMvcTest(StudentApiController.class)
class StudentApiControllerTest {

    @Autowired
    private MockMvc mvc;

    @MockBean
    private StudentManagementService managementService;

    @Test
    @DisplayName("모든 학생 목록 조회 성공")
    void getAllStudents_Success() throws Exception {
        mvc.perform(get("/api/students/list"))
           .andExpect(status().isOk())
           .andExpect(content().contentType(MediaType.APPLICATION_JSON))
           .andExpect(jsonPath("$[?(@.name == 'anonymous')]").exists())
           .andDo(print());

        verify(managementService, times(1)).loadAll();
    }

    @Test
    @DisplayName("데이터 임포트 요청 처리 확인")
    void importPayload_ValidatesExecution() throws Exception {
        String testData = "{\"file\":\"data.csv\"}";

        mvc.perform(post("/api/students/import")
                .contentType(MediaType.APPLICATION_JSON)
                .content(testData))
           .andExpect(status().isAccepted())
           .andDo(print());

        verify(managementService, times(1)).processImport(testData);
    }
}
    

주요 어노테이션 설명

  • @WebMvcTest: 웹 계층에 집중한 테스트를 위한 어노테이션으로, 컨트롤러와 관련된 빈만 로드하여 테스트를 가볍게 만든다. @SpringBootTest보다 더 효율적이다.
  • @MockBean: 애플리케이션 컨텍스트 내에 존재하는 빈을 Mockito 목으로 교체한다. 여기서는 실제 DB 접근 없이 서비스 호출 여부만 검증한다.
  • @Autowired MockMvc: @WebMvcTest와 함께 사용하면 자동으로 구성되며, HTTP 요청을 시뮬레이션할 수 있다.

MockMvc 요청 및 검증 흐름

각 테스트는 다음과 같은 흐름으로 진행된다:

  1. mockMvc.perform()으로 HTTP 메서드와 경로 지정
  2. .contentType(), .content() 등으로 요청 본문과 타입 설정
  3. .andExpect()으로 응답 상태, 콘텐츠, JSON 필드 등을 검증
  4. .andDo(print())로 콘솔에 전체 요청/응답 출력 (디버깅 용도)
  5. 마지막으로 Mockito를 사용해 서비스 메서드 호출 횟수 검증

태그: Spring Boot MockMvc JUnit 5 Mockito WebMvcTest

6월 13일 17:41에 게시됨