스프링부트

Spring Boot 테스트 - MockMvc 알아보기

알쓸개잡 2025. 11. 22.

Spring Boot 애플리케이션에서 Controller를 테스트할 때 가장 널리 사용되는 도구가 MockMvc와 WebTestClient다. Spring Boot 3.X부터는 WebTestClient 사용 빈도가 점점 증가하고 있는 추세라고 하지만 MVC 모델에서는 아직도 MockMvc를 많이 사용하고 있다. 이번 포스팅에서는 MockMvc 사용법에 대해서 정리해 보고자 한다.

 

MockMvc

Spring MVC 기반 애플리케이션에서 Controller를 HTTP 요청 없이 테스트하기 위해서 등장했다. Servlet Container(Tomcat/Jetty와 같은)를 실제로 실행하지 않고 DispatchServlet 레벨에서 요청을 처리할 수 있다.

 

주요 특징은 다음과 같다.

  • Spring MVC 전용 (WebFlux 환경은 테스트가 불가하다)
  • 서버 실행 없이 DispatchServlet을 직접 호출하기 때문에 매우 빠르다.
  • @WebMvcTest와 함께 사용하면 슬라이스 테스트가 가능하다.
  • JSONPath, XPath, Validation 검증을 지원한다.

MockMvc 동작 구조는 다음과 같다.

MockMvc -> DispatchServlet -> HandlerMapping -> Controller -> ViewResolver

 

MockMvc perform

MockMvc perform이 제공하는 역할을 살펴보면 다음에 설명 할 MockMvcRequestBuilders와 MockHttpServletRequestBuilder를 통해서 요청을 전송하고 응답을 기반으로 검증 및 동작을 체이닝 형태로 수행할 수 있다.

perform 메서드는 ResultActions 구현체를 리턴하는데 그 구현체는 다음과 같이 정의된다.

return new ResultActions() {
    @Override
    public ResultActions andExpect(ResultMatcher matcher) throws Exception {
        matcher.match(mvcResult);
        return this;
    }
    @Override
    public ResultActions andDo(ResultHandler handler) throws Exception {
        handler.handle(mvcResult);
        return this;
    }
    @Override
    public MvcResult andReturn() {
        return mvcResult;
    }
};

andExpect()와 andDo()는 this를 리턴함으로써 체이닝을 구성할 수 있다. andExpect와 andDo는 각각 ResultMatcher, ResultHandler 함수형 인터페이스를 파라미터로 전달받고 이 함수형 인터페이스에 mvcResult (응답 결과) 인스턴스를 전달하여 해당 함수형 인터페이스 내에서 검증 및 동작을 수행하도록 한다.

미리 정의된 ResultMatcher와 ResultHandler를 생성하도록 하는 팩토리 역할을 하는 클래스를 제공하는데 이것이 바로 다음에 설명할 MockMvcResultMatchers와 MockMvcResultHandlers다.

andReturn()은 MockMvc 테스트에서 응답 이후에 응답 결과(MvcResult)를 직접 받아서 후속 처리를 하고 싶을 때 사용할 수 있다.

andReturn은 주로 비동기 요청 테스트에 사용되는데 그 외에는 크게 사용될 일은 없을 것 같다.

@GetMapping("/async")
public WebAsyncTask<User> asyncTask() {
    Callable<User> callable = () -> {
        Thread.sleep( 1000 );
        return new User( 1L, "name1", User.Gender.MALE, 10 );
    };

    return new WebAsyncTask<>( callable );
}
@Test
void async_test() throws Exception {
    MvcResult mvcResult = mockMvc.perform( get( "/mvc/users/async" ) )
            .andExpect( request().asyncStarted() )
            .andReturn();

    mockMvc.perform( asyncDispatch( mvcResult ) )
            .andExpect( status().isOk() )
            .andExpect( jsonPath( "$.id" ).value( 1 ) )
            .andExpect( jsonPath( "$.name" ).value( "name1" ) )
            .andExpect( jsonPath( "$.gender" ).value( User.Gender.MALE.name() ) )
            .andExpect( jsonPath( "$.age" ).value( 10 ));
}

 

perform()에서 리턴하는 ResultActions의 각 메서드 역할을 요약하면 다음과 같다.

  • andExpect()는 검증을 수행
  • andDo()는 로그 출력등의 부수적인 작업
  • andReturn()은 응답 전체 결과(MvcResult)를 반환

MockMvc 주요 구성 요소

MockMvc를 사용하기 위한 주요 구성 요소는 다음과 같다.

MockMvcBuilders

MockMvc 환경을 코드적으로 구성할 때 사용한다.

class MyWebTests {

    MockMvc mockMvc;

    @BeforeEach
    void setup() {
        this.mockMvc = MockMvcBuilders.standaloneSetup(new AccountController()).build();
    }

    // ...
}

@WebMvcTest({controller 클래스 지정}) 어노테이션을 통해서 MockMvc 인스턴스를 자동으로 주입받아 사용하는 방식도 있다.

@WebMvcTest({ControllerA.class, ControllerB.class})
class UserControllerMVCTest {
    @Autowired
    MockMvc mockMvc;
    ...
    ...
}

 

MockMvcBuilders는 webAppContextSetup, standaloneSetup 메서드가 제공되는데 각각 사용 용도는 다음과 같다.

  • webAppContextSetup 
    • Spring Application Context에 로딩된 모든 컨트롤러에 대해서 테스트를 할 수 있다.
    • Spring MVC 구성을 로드하므로 보다 완벽한 통합 테스트를 수행할 수 있다.
    • @MockitoBean 혹은 @TestBean을 사용하여 컨트롤러가 사용하는 서비스를 오버라이드하여 웹 계층 테스트에 집중할 수 있다.
  • standaloneSetup
    • 지정한 컨트롤러 클래스에 대해서 테스트 할 수 있다.
    • 유닛 테스트에 조금더 가깝다. 한 번에 하나의 컨트롤러만 테스트하며 컨트롤러에 모의 의존성을 수동으로 주입할 수 있어 Spring 구성 로딩이 불필요하다. 

MockMvcBuilders 구현체는 공통적이고 유용한 기능들을 제공한다. 예를 들어, 다음과 같이 모든 요청에 대해 Accept 헤더를 선언하고 상태코드 200을 기대하고 모든 응답에 대한 Content-Type 헤더를 자동으로 검증하도록 할 수 있다.

@BeforeEach
void setup() {
    this.mockMvc = MockMvcBuilders.standaloneSetup( new UserControllerMVC() )
            .defaultRequest( get( "/" ).header( "X-API-KEY", "my-default-key" ) )
            .alwaysDo( MockMvcResultHandlers.print() )
            .alwaysExpect( status().isOk() )
            .alwaysExpect( content().contentType( MediaType.APPLICATION_JSON ) )
            .build();
}

.defaultRequest(get("/").header("X-API-KEY", "my-default-key")에서 GET / 요청에 대해서만 적용되는 것이라 생각할 수도 있는데 defaultRequest는 모든 요청에 적용되는 기본 Request 템플릿을 정의하는 것이고 get("/")는 기본 RequestBuilder를 만드는데 필요한 "그냥 기본 형식"일 뿐이다. 만약 defaultRequest를 정의하여 모든 요청에 대해서 특정 헤더를 넣고 싶을 때 이를 생성하기 위해서는 어떤 메서드라도 써야 하는데 get("/")는 가장 단순한 형태이므로 형식적으로 사용한 것뿐이라는 것이다.

@Test
void get_user_test() throws Exception {
    int id = 1;
    mockMvc.perform( get("/mvc/users/{id}", id)
                    .contentType( MediaType.APPLICATION_JSON ) )
            .andExpect( jsonPath( "$.id" ).value( 1 ) )
            .andExpect( jsonPath( "$.name" ).value( "name1" ) )
            .andExpect( jsonPath( "$.gender" ).value( "MALE" ) )
            .andExpect( jsonPath( "$.age" ).value( 10 ))
            .andExpect( header().string( "Content-Type", is(equalTo(MediaType.APPLICATION_JSON_VALUE))) );
}

위 테스트 코드는 요청시 "X-API-KEY" 헤더를 자동으로 추가하고 요청 응답에 대한 상세 내용을 출력하며 응답 상태 코드와 contentType 값을 추가로 검증한다. (mockMvc에 공통으로 적용되는 alwaysDo, alwaysExpect에 의해서)

 

MockMvcRequestBuilders

MockMvcRequestBuilders는 MockMvc에서 HTTP 요청을 생성하기 위한 빌더 클래스다. get(), post(), patch()와 같은 static 메서드와 함께 요청을 생성하는 데 사용한다.

메서드 시그니처

// get 요청
public static MockHttpServletRequestBuilder get(String uriTemplate, Object... uriVariables)
public static MockHttpServletRequestBuilder get(URI uri)
// post 요청
public static MockHttpServletRequestBuilder post(String uriTemplate, Object... uriVariables)
public static MockHttpServletRequestBuilder post(URI uri)
// put 요청
public static MockHttpServletRequestBuilder put(String uriTemplate, Object... uriVariables)
public static MockHttpServletRequestBuilder put(URI uri)
// patch 요청
public static MockHttpServletRequestBuilder patch(String uriTemplate, Object... uriVariables)
public static MockHttpServletRequestBuilder patch(URI uri)
// delete 요청
public static MockHttpServletRequestBuilder delete(String uriTemplate, Object... uriVariables)
public static MockHttpServletRequestBuilder delete(URI uri)
// options 요청
public static MockHttpServletRequestBuilder options(String uriTemplate, Object... uriVariables)
public static MockHttpServletRequestBuilder options(URI uri)
// head 요청
public static MockHttpServletRequestBuilder head(String uriTemplate, Object... uriVariables)
public static MockHttpServletRequestBuilder head(URI uri)
// 메서드를 직접 지정해서 요청
public static MockHttpServletRequestBuilder request(HttpMethod method, String uriTemplate, Object... uriVariables)
public static MockHttpServletRequestBuilder request(HttpMethod httpMethod, URI uri)
// multipart 요청
public static MockMultipartHttpServletRequestBuilder multipart(String uriTemplate, Object... uriVariables)
public static MockMultipartHttpServletRequestBuilder multipart(HttpMethod httpMethod, String uriTemplate, Object... uriVariables)
public static MockMultipartHttpServletRequestBuilder multipart(URI uri)
public static MockMultipartHttpServletRequestBuilder multipart(HttpMethod httpMethod, URI uri)

(String uriTemplate, Object... uriVariables) 시그니처를 갖는 메서드는 PathVariable을 resolve 한다.

get("/mvc/users/{id}", id)

변수 id가 /mvc/users/{id} 내의 {id} path variable을 resolve 한다.

 

MockMvcRequestBuilders 메서드 자체는 요청 빌더를 생성하는 역할을 하고 세부적인 설정은 MockHttpServletRequestBuilder에서 한다.

mockMvc.perform( get( "/mcv/users/{id}", id )
                        .queryParam( "gender", User.Gender.FEMALE.name() )
                        .contentType( MediaType.APPLICATION_JSON ) )
                        .header("X-header", "aaa")
                        .characterEncoding(StandardCharsets.UTF_8)
                        .cookie(...)
                        .content(...)
                        .accept(...)

queryParam: 쿼리 파라미터 지정

contentType: Content-Type 헤더 지정

header: 추가 헤더 지정

cookie: 쿠키 설정

accept: Accept 헤더 지정

content: POST / PUT /PATCH body payload 지정

characterEncoding: 캐릭터 인코딩 지정

 

파일 업로드 테스트

multipart 요청을 생성하여 처리할 수 있다. 물론 실제로 업로드를 하는 것은 아니고 업로드 하는 것처럼 흉내 내는 것이다.

multipart()는 MockMultipartHttpServletRequestBuilder 인스턴스를 리턴하는데 MockHttpServletRequestBuilder를 상속한다. 즉, MockHttpServletRequestBuilder에서 제공하는 상세 요청 설정을 그대로 할 수 있다. MockHttpServletRequestBuilder의 디폴트 HttpMethod는 POST이고 contentType은 multipart/form-data다.

 

@Test
void multipart_test() throws Exception {
    try( InputStream is = new FileInputStream( "src/test/resources/fruits.csv" ) ) {
        MockMultipartFile mockMultipartFile = 
            new MockMultipartFile( "file", "fruits.csv", "text/csv", is );
            
        mockMvc.perform( multipart( "/mvc/users/upload" )
                .file( mockMultipartFile ) )
                .andExpect( status().isOk() );
    }
}

MockMultipartFile 클래스를 통해서 업로드할 파일을 설정한다. MockMultipartFile 생성자의 마지막 필드에는 InputStream을 넘길 수도 있고 byte[]를 넘길 수도 있다.

 

MockMvcResultMatchers

요청에 대한 응답 데이터를 검증하는데 사용한다. MockMvcResultMatchers는 검증을 지원하는 여러 Matcher 클래스를 제공한다.

해당 Matcher 클래스들은 ResultMatcher 인터페이스를 리턴하는 각종 메서드들이 있는데 ResultMatcher는 함수형 인터페이스로 다음과 같이 match가 정의되어 있다.

void match(MvcResult result) throws Exception;

MvcResult는 요청에 대한 응답 결과를 담고 있는 인스턴스이고 ResultMatcher 함수형 인터페이스에서는 MvcResult 인스턴스를 통해 검증을 수행한다.

이 여러가지 검증들을 수행하는 Matchers 클래스들을 제공하는 역할을 하는 클래스가 바로 MockMvcResultMatchers다. 검증 절차는 다음과 같다.

MockMvcResultMatchers 
    -> Matchers 제공 
        -> ResultMatcher 인스턴스 제공 
            -> MockMvc.andExpect 에서 ResultMatcher를 호출하여 검증 (여기서 MvcResult를 전달)

 

예를 들어 MockMvcResultMathers.status() 메서드는 다음과 같이 정의되어 있다.

public static StatusResultMatchers status() {
    return new StatusResultMatchers();
}

StatusResultMatchers() 인스턴스를 리턴하는데 StatusResultMatchers 클래스의 isOk() 메서드를 살펴보자.

public ResultMatcher isOk() {
    return matcher(HttpStatus.OK);
}

===========

// matcher()는 다음과 같이 정의되어 있다.
// 여기서 result는 MvcResult 인스턴스(응답 결과)가 전달된다.
private ResultMatcher matcher(HttpStatusCode status) {
    return result -> assertEquals("Status", status.value(), result.getResponse().getStatus());
}

StatusResultMatchers 클래스의 matcher() 메서드는 ResultMatcher 함수형 인터페이스 인스턴스를 생성한다.

즉, isOk() 메서드는 응답 결과의 상태 코드가 HttpStatus.OK.value() 값(200) 인지 여부를 검증하는 동적 메서드를 생성하는 것이다.

MockMvc의 andExpect를 사용하는 부분을 살펴보자.

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;

@Test
void get_user_test() throws Exception {
    int id = 1;
    mockMvc.perform( get("/mvc/users/{id}", id)
                    .contentType( MediaType.APPLICATION_JSON ) )
            .andExpect( MockMvcResultMatchers.status().isOk() )
}

위 코드는 결론부터 말하자면 /mvc/users/1을 호출한 결과 응답 코드가 200인 것을 체크하는 테스트 코드다.

앞에서 설명했듯이 MockMvc 클래스의 perform 메서드는 ResultActions를 리턴한다. ResultActions에 정의된 andExpect와 andDo 메서드는 this를 리턴하여 체이닝을 할 수 있다.

mockMvc.andExpect( MockMvcResultMatchers.status().isOk() )에서 MockMvcResultMatchers.status().isOk() 호출은 앞에서 언급했듯이 응답 상태코드가 200인지 여부를 체크하는 동적 메서드 (ResultMatcher 함수형 인터페이스의 인스턴스)를 생성한다.

최종적으로 mockMvc.andExpect( MockMvcResultMatchers.status().isOk() ) 호출은

@Override
public ResultActions andExpect(ResultMatcher matcher) throws Exception {
    matcher.match(mvcResult);
    return this;
}

위 코드에 의해서 동적 생성된 메서드에 mcvResult 인스턴스(응답 결과)를 전달하여 검증을 수행한다.

 

jsonPath()호출하는 예를 보자.

만약 테스트 컨트롤러가 다음과 같은 body payload를 응답한다고 했을 때

{
    "id": 1,
    "name": "name1",
    "gender": "MALE",
    "age": 10
}
 @Test
void get_user_test() throws Exception {
    int id = 1;
    mockMvc.perform( get("/mvc/users/{id}", id)
                    .contentType( MediaType.APPLICATION_JSON ) )
            .andExpect( MockMvcResultMatchers.status().isOk() )
            .andExpect( MockMvcResultMatchers.jsonPath( "$.id" ).value( 1 ) )
            .andExpect( MockMvcResultMatchers.jsonPath( "$.name" ).value( "name1" ) )
            .andExpect( MockMvcResultMatchers.jsonPath( "$.gender" ).value( "MALE" ) )
            .andExpect( MockMvcResultMatchers.jsonPath( "$.age" ).value( 10 ));
}

MockMvcResultMatchers.jsonPath는 JsonPathResultMatchers 인스턴스를 생성하고 value()는 다음과 같은 함수형 인터페이스를 리턴한다.

public ResultMatcher value(@Nullable Object expectedValue) {
    return result -> this.jsonPathHelper.assertValue(getContent(result), expectedValue);
}

즉 jsonPath로 지정된 필드의 값과 expectedValue의 값이 같은지를 검증하는 동적 메서드를 생성한다.

이 동적 메서드는 mocvMvc.andExpect의 인자로 넘겨져서 mvcResult를 기반으로 검증을 수행하게 된다.

 

또한 다음과 같이 ResultMatcher를 직접 생성해서 검증해도 된다.

@Test
void get_user_test() throws Exception {
    int id = 1;
    mockMvc.perform( get("/mvc/users/{id}", id)
                    .contentType( MediaType.APPLICATION_JSON ) )
            .andExpect( result -> {
                int status = result.getResponse().getStatus();
                Assertions.assertEquals( HttpStatus.OK.value(), status );
            } )
}

위 코드에서 andExpect 내에 정의된 lambda 함수가 직접 정의한 ResultMatcher 인스턴스가 되겠다.

 

org.springframework:spring-test 모듈의 org.springframework.test.web.servlet.result.MockMvcResultMatcher.java 클래스에 보면 여러 유형의 Matcher 클래스를 확인해 볼 수 있다.

정적 메서드 Matchers 타입 검증 주요 Matchers의 메서드
request() RequestResultMatchers 요청 자체에 대한 검증 attribute(String name, Object expectedValue)
attributeExists(String name)
...
handler() HandlerResultMatchers 컨트롤러 매핑 검증 methodName(String name)
handlerType(Class<?> type)
...
model() ModelResultMatchers Model attribute 검증 attributeExists(String name)
attribute(String name, Object value)
...
view() ViewResultMatchers 뷰 이름 검증 (Spring MVC 서버 템플릿 사용시) name(String expectedViewName)
flash() FlashAttributeResultMatchers redirect시 전달된 flash attribute 검증 attribute(String name, Object value)
attributeExists(String ...names)
attributeCount(int count)
forwardedUrl() unnamed ResultMatcher forward URL 검증 URL 일치 기반으로 검증한다.
forwardedUrlPattern() unnamed ResultMatcher forward URL 검증 패턴 기반으로 URL을 검증한다.
forwardedUrlTemplate() unnamed ResultMatcher forward URL 검증 Path Variable 기반으로 URL을 검증한다.
ex) ("/users/{id}", 10) -> /users/10
redirectedUrl() unnamed ResultMatcher redirect URL 검증 URL 일치 기반으로 검증한다.
redirectedUrlPattern() unnamed ResultMatcher redirect URL 검증 패턴 기반으로 URL을 검증한다.
redirectedUrlTemplate() unnamed ResultMatcher redirect URL 검증 Path Variable 기반으로 URL을 검증한다.
ex) ("/users/{id}", 10) -> /users/10
status() StatusResultMatcher HTTP 응답 코드 검증 isOk()
is(int status)
is1xxInformational()
is2xxSuccessful()
is3xxRedirection()
is4xxClientError()
is5xxServerError()
...
content() ContentResultMatcher 응답 body
content-type 검증
contentType(String contentType)
contentType(MediaType contentType)j
encoding(String characterEncoding)
string(String expectedContent)
json(String jsonContent)
json(String, JsonCompareMode)
...
header() HeaderResultMatcher 응답 헤더 검증 string(String name, String value)
stringValues(String name, String ...values)
exists(String name)
doesNotExist(String name)
longValue(String name, long value)
dateValue(String name, long value)
jsonPath() JsonPathResultMatchers JSON 필드 검증 "$.name" 형식으로 필드 지정
value(Object expectedValue)
exists()
doesNotExist()
isEmpty()
hasJsonPath()
isString()
isBoolean()
...
xpath() XpathResultMatchers XML 특정 노드 검증 exists()
doesNotExist()
nodeCount(int expectedCount)
string(String expectedValue)
...
cookie() CookieResultMatchers 쿠키 값 검증 value(String name, String expectedValue)
exists(String name)
doesNotExist(String name)
maxAge(String name, int maxAge)
path(String name, String path)
...

 

MockMvcResultHandlers

perform 메서드가 리턴하는 ResultActions 에는 andDo 메서드가 정의되어 있는데 이 때 전달해야 하는 파라미터는 ResultHandler 함수형 인터페이스를 전달해야 한다. 보통은 상세 정보를 로깅하는 데 사용되는데 MockMvcResultHandlers의 static 메서드를 통해서  ResultHandler 구현체 클래스를 받아서 사용할 수 있다.

public static ResultHandler log() {
    return new LoggingResultHandler();
}

public static ResultHandler print() {
    return print(System.out);
}

public static ResultHandler print(OutputStream stream) {
    return new PrintWriterPrintingResultHandler(new PrintWriter(stream, true));
}

public static ResultHandler print(Writer writer) {
    return new PrintWriterPrintingResultHandler(new PrintWriter(writer, true));
}

LoggingResultHandler와 PrintWriterPrintingResultHandler는 모두 ResultHandler의 구현체다. 각각의 모두 응답 결과에 대한 상제 정보를 제공한다.

@Test
void get_user_test() throws Exception {
    int id = 1;
    mockMvc.perform( get("/mvc/users/{id}", id)
                    .contentType( MediaType.APPLICATION_JSON ) )
            .andDo( MockMvcResultHandlers.print() )
            // ResultHander 함수형 인터페이스를 직접 정의해도 된다.
            .andDo( result -> {
                System.out.println( "status: " + result.getResponse().getStatus());
                System.out.println( result.getResponse().getContentAsString() );
            } )
            .andExpect( status().isOk() )
}

andDo에서 MockMvcResultHandlers.print()를 호출하여 PrintWriterPrintingResultHandler를 넘겨주어 응답에 대한 상세 정보를 System.out 으로 기록하도록 했다.

MockMvcResultHandlers.log()를 호출하여 로그를 기록하려면 test/resources에 logback-test.xml 파일을 생성한다.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n</pattern>
            <charset>utf8</charset>
        </encoder>
    </appender>

    <!-- Enable MockMvcResultHandlers.log() output -->
    <logger name="org.springframework.test.web.servlet.result" level="DEBUG" additivity="false">
        <appender-ref ref="STDOUT"/>
    </logger>

    <!-- You can bump this to DEBUG if you want more noise globally during tests -->
    <root level="INFO">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

출력 되는 정보는 다음과 같다.

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /mvc/users/1
       Parameters = {}
          Headers = [Content-Type:"application/json"]
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = org.example.spring.boot.test.controller.UserControllerMVC
           Method = org.example.spring.boot.test.controller.UserControllerMVC#getUser(Long)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json"]
     Content type = application/json
             Body = {"id":1,"name":"name1","gender":"MALE","age":10}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

MockMvc 테스트 시 json body는 객체를 전달할 수 없다

MockMvc로 POST/PUT/PATCH를 테스트하는 경우 body payload를 전달해야 하는 경우가 있다. MockMvc는 내부적으로 JSON변환을 자동으로 수행하지 않기 때문에 JSON 문자열로 body payload를 전달해야 한다. 이 경우 ObjectMapper(혹은 Gson)를 사용하여 간단히 처리할 수 있다.

Controller

@RestController
@RequestMapping("/mvc/users")
public class UserControllerMVC {
    @PostMapping
    public ResponseEntity<User> createUser( @RequestBody User user ) {
        return ResponseEntity.ok( user );
    }
}

테스트 코드

@Test
void post_test() throws Exception {
    ObjectMapper objectMapper = new ObjectMapper();

    Long id = 1L;
    String name = "testname";
    int age = 40;
    User.Gender gender = User.Gender.MALE;

    User user = new User( id, name, gender, age );
    mockMvc.perform(
                    post( "/mvc/users" )
                            .contentType( MediaType.APPLICATION_JSON )
                            // user 자체를 전달 할 수 없으므로 JSON문자열로 변환해야 한다.
                            .content( objectMapper.writeValueAsString( user ) )
            )
            .andDo( MockMvcResultHandlers.print() )
            .andExpect( status().isOk() )
            .andExpect( handler().handlerType( UserControllerMVC.class ) )
            .andExpect( handler().methodName( "createUser" ) )
            .andExpect( jsonPath( "$.id" ).value( 1 ) )
            .andExpect( jsonPath( "$.name" ).value( name ) )
            .andExpect( jsonPath( "$.gender" ).value( gender.name() ) )
            .andExpect( jsonPath( "$.age" ).value( age ) );
}

 

끝.

댓글

💲 추천 글