본문 바로가기
개발/스프링

[Spring] MockMVC를 이용한 Controller 테스트 (GET)

by 카펀 2022. 6. 20.

개요

Spring 환경에서 테스트 코드를 작성할 때 자주 사용하게 되는 MockMVC, 그 중에서도 GET에 대해 아주 간단히 다루어 보겠습니다.

 

목차

  1. 배경
  2. MockMVC 소개
  3. MockMVC를 이용한 Controller GET 테스트
  4. MockMVC GET 자세히 알아보기

1. 배경

업무 중 특정 기록을 조회하는 기능을 개발하고 있었습니다. 앞단에서 검색 파라미터 (칼럼별 속성, 검색기간, 페이징 개수 등)를 설정해서 넘겨 주면, Controller -> Service -> DAO -> MyBatis 쿼리를 실행하는 루틴을 전부 개발하게 되었습니다.

개발한 컨트롤러가 의도대로 동작하는지 확인하려면 어떻게 해야 할까요? 테스트 코드가 없다면, Tomcat을 띄우고, 로컬 환경에서 사이트에 접속한 후, 해당 페이지에서 조회를 직접 확인해 봐야 합니다.

만약 조회하는 화면이 아직 완성되지 않았다면? 별도로 검증할 수단이 없습니다.

제가 처한 상황이 이랬습니다. 다른 개발자분이 앞단 개발을 진행하시는 동안, 저는 위에서 언급한 백엔드 내용을 개발해야 했고, 이를 단위 테스트를 통해 검증할 필요성을 느꼈습니다.

하지만 테스트 환경에서는 실제 조회시와는 다르게 Controller로 요청이 날아오지 않습니다. 따라서 테스트를 위한 가짜 요청을 만들어야 하며, 이를 MockMVC가 담당합니다.

 

2. MockMVC란

MockMVC란, 개발한 웹 프로그램을 실제 서버에 배포하지 않고도 테스트를 위한 요청을 제공하는 수단입니다. GET, POST, PATCH, DELETE 등의 요청을 만들어 보낼 수 있습니다.

MockMVC를 이용한 테스트는 단위 테스트와 통합 테스트 사이의 테스트라고 합니다.

 

3.MockMVC를 이용한 Controller 테스트

다음과 같은 환경에서 진행했습니다.

  • Spring Boot 2.6.3
  • JUnit 5.8.2
  • Java 15

꼭 위와 같은 환경이 아니더라도, MockMVC를 이용한 테스트는 여전히 수행할 수 있습니다.

 

아래와 같이 테스트용 컨트롤러를 작성해 보았습니다.

 

@RestController
public class postsListController {

    @RequestMapping("/postsList")
    public String getPostsList() {
        return "This will return posts's URI.";
    }
}

"/" 라는 GET 요청을 받으면, home이라는 메소드가 이 요청을 받고, "home" 이라는 문자열을 반환합니다.

JSON 형태의 결과를 리턴하는 @RestController로 작성하였습니다.

 

이 컨트롤러를 테스트 헤 보겠습니다.

@Autowired
postsListController postsListController;

@Autowired
private MockMvc mockMvc;
    
@Test
public void getPostsList() throws Exception {
    mockMvc.perform(
            MockMvcRequestBuilders.get("/postsList"))
            .andExpect(status().isOk())
            .andExpect(content().string("This will return posts's URI."))
            ;
}

MockMVC를 생성하고, '/postsList' 라는 URI로 GET 요청을 보냅니다.

이후 응답 상태가 정상이며, 반환받는 문자열이 "This will return posts's URI." 라면 테스트가 성공합니다.

 

실제로 작성한 컨트롤러는 그런 역할을 합니다.

'/postsList'라는 URI와 매핑되어 있고, 문자열 "This will return posts's URI." 를 리턴합니다.

 

테스트 성공

 

이는 MockMVC를 이용한 아주 기초적인 테스트 코드입니다.

 

4. MockMVC 자세히 알아보기

MockMVC에 대해 자세한 내용은 이 글을 참고해 주세요.

앞에서는 아주 간단한 MockMVC 소개와 함께, 굉장히 기초적인 GET 테스트를 진행해 보았습니다.

 

MockMVC는 훨씬 다양하게 테스트해 볼 수 있습니다.

파라미터를 Service 단으로 넘겨 주어 동적 결과를 받아올 수 있고, 앞서 언급한 바와 같이 GET 외에도 PUT, POST 등을 테스트 할 수 있습니다.

 

GET 테스트의 경우, 아래와 같은 다양한 옵션을 설정할 수 있습니다.

  • get: 지정한 URI로 GET 요청을 보냅니다.
  • andDo(print()): GET 요청의 결과를 콘솔에 출력합니다.
  • andExpect: assertThat과 유사하게, 특정 결과가 나타나도록 기대합니다. 그렇지 않은경우 assertionError를 얻습니다.
  • params: key-value 형태로 GET 호출 시 값을 넘겨줍니다.
@Test
public void getAllEmployeesAPI() throws Exception 
{
  mvc.perform( MockMvcRequestBuilders
      .get("/employees")
      .accept(MediaType.APPLICATION_JSON)
      .andDo(print())
      .andExpect(status().isOk())
      .andExpect(MockMvcResultMatchers.jsonPath("$.employees").exists())
      .andExpect(MockMvcResultMatchers.jsonPath("$.employees[*].employeeId").isNotEmpty());
}
 
@Test
public void getEmployeeByIdAPI() throws Exception 
{
  mvc.perform( MockMvcRequestBuilders
      .get("/employees/{id}", 1)
      .accept(MediaType.APPLICATION_JSON)
      .andDo(print())
      .andExpect(status().isOk())
      .andExpect(MockMvcResultMatchers.jsonPath("$.employeeId").value(1));
}

(출처: 링크)
위 두 메소드는 각각 호출하는 URI가 정적인 경우와 동적인 경우를 나타냅니다.

동적 URI로 요청을 보내는 경우, 위와 같이 파라미터를 지정 및 넘겨줄 값을 정할 수 있습니다.

 

참고로 위에서 만든 getPostList에 .andDo(print())를 추가하면, 아래와 같은 결과를 얻게 됩니다.

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /postsList
       Parameters = {}
          Headers = []
             Body = null
    Session Attrs = {}

Handler:
             Type = com.tistory.katfun.crud.posts.postsListController
           Method = com.tistory.katfun.crud.posts.postsListController#getPostsList()

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:"text/plain;charset=UTF-8", Content-Length:"29"]
     Content type = text/plain;charset=UTF-8
             Body = This will return posts's URI.
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

보통 테스트코드를 눈으로 검증하는 것이 좋은 방법은 아니지만, 특정 테스트 코드의 결과를 상세히 보고 싶다면 위처럼 print를 사용하는 것도 좋은 방법입니다.

댓글