[test] Controller 테스트와 MockMvc

2022. 4. 7. 10:49[ 백엔드 개발 ]/[ Spring ]

모델 단을 단위 테스트하는 것과 비슷하게, 컨트롤러도 Mockito와 Junit을 이용해 단위 테스트할 수 있다. 하지만 이 경우 실질적인 HTTP 제이슨 페이로드를 받아 검증하는 방식은 아니다. @Vaild와 같은 어노테이션을 포함한 다양한 것들을 검증하기 위해서는 Mockito와 Junit 만으로는 부족하다.

 

이러한 경우를 대비해 스프링이 MockMvc를 제공하고 이를 활용해 컨트롤러 end point를 적용한 테스트를 진행할 수 있다. 즉, MockMvc를 활용하면 모의 서블릿 환경에 대한 테스트를 작성할 수 있다. 또한 Mockito에서 제공하는 @Mock이나 @InjectMocks와 같은 어노테이션은 스프링 컨텍스트에 로드하지 않기 때문에 컨트롤러 테스트에서는 사용될 수 없다. 이때는 @Mockbean을 활용해 컨트롤러와 의존성을 갖는 목 객체를 스프링 컨텍스트에 올리고 컨트롤러에 주입한다.

 

따라서 컨트롤러를 테스트할 때에는 Mockito의 @MockBean을 활용해 목 객체를 주입하고 MockMvc를 추가적으로 활용하여 가상의 스프링 컨텍스트를 만들며 컨트롤러로 들어오는 HTTP 모의 요청을 만들고 컨트롤러가 응답하는 결과를 기반으로 테스트한다.

 

MockMvc를 사용한다고 해서 실질적으로 HTTP통신이 발생하는 것은 아니지만 요청을 수행하고 모의 서블릿 환경을 직접 호출하는 HTTP header와 body를 포함시킬 수 있다. 

 

test용 dispatcher servlet

 

MockMvc는 DispatcherServlet을 상속(확장)하는 TesDispatcherServlet을 생성한다.

 

 

 

cf. MockMvc는 springframework에 포함되며 spring-boot-starter-test 모듈에서도 제공됨

 

 

MockMvc의 기능

MockMvc는 크게 3가지 기능을 갖는다.

1) perform : MockMvcRequestBuilders를 통해 생성된 가상의 request를 컨트롤러로 호출

2) expect : 생성된 가상의 response를 검증

3) do : 출력과 같이 직접 처리해줘야 할 일을 담당

 

MockMvc로 컨트롤러 테스트 과정

1. 요청 만들고 컨트롤러 호출

MockMvcRequestBuilders의 static 메소드인 get(), post(), put(), delete(), fileUpload() 등을 이용해 가상의 요청을 만든다. 생성된 가상의 요청 객체는 MockHttpServletRequestBuilder 객체이다. builer과정에서 요청에 필요한 param(), params(), header(), headers(), contentType(), cookie() 등이 지원된다.

 

만들어진 가상의 request를 MockMvc의 perform() 메소드의 인자로 보내 컨트롤러를 호출한다.

 

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/web/servlet/MockMvc.html#perform-org.springframework.test.web.servlet.RequestBuilder-

 

cf. MockMvcRequestBuilders문서: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/web/servlet/request/MockMvcRequestBuilders.html

 

2. 검증하기

perform() 메소드는 ResultActions을 반환한다. ResultActions의 andExpect() 메소드에 ResultMatcher를 넘겨줌으로써 검증 과정을 처리한다. 

ResultActions의 andExpect() 메소드는 ResultActions를 반환하기 때문에 chaining으로 다수의 ResultMatcher를 검증할 수 있다.

 

 

cf. ResultMatcher를 얻는 과정

MockMvcResultMatchers의 다양한 static 메소드를 통해 얻는다. 

 

cf. MockMvcResultMatchers 문서: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/web/servlet/result/MockMvcResultMatchers.html

 

 

 

3. 실행하기

검증하는 과정에서 ResultActions의 andExpect() 메소드를 이용했듯이, 실행할 때에는 ResultActions의 andDo()를 활용한다.

 

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/web/servlet/ResultActions.html#andDo-org.springframework.test.web.servlet.ResultHandler-

 

andDo() 메소드의 파라미터로 ResultHandler를 받는데, 이는 MockMvcResultHandler를 통해 얻는다.

 

MockMvcResultHandler

MockMvcResultHandler에 정의된 static 메소드는 대부분 출력을 담당한다. 기본 print()를 활용하면 표준 출력(System.out)으로 출력한다.

 

 


 

MockMvc 설정

모델 단을 테스트하는 것은 개발자가 직접 작성한 비즈니스 로직을 테스트하기 때문에 스프링 컨텍스트를 로드할 필요 없이 가능하다. 하지만 컨트롤러 단은 서블릿과 연관되어 있기 때문에 security 등을 비롯해 다양한 설정 정보가 필요하다. 따라서 MockMvc를 사용할 경우 스프링 컨텍스트를 로드하고 어떤 filter를 적용해 테스트할지 결정해야 한다.

 

만약 이러한 설정 없이 모든 bean을 컨텍스트에 띄워 테스트하고자 한다면 @SpringBootTest 어노테이션을 사용하면 된다. 적용할 필터들을 MockMvc에 알려줘야 하고 이러한 정보들은 @AutoConfigureMockMvc 어노테이션을 통해 가능하다. 만약 필터를 적용하려면 @WebMvcTest 어노테이션의 includeFilters 필드를 사용해 지정한다. config 파일이 갖는 의존성은 @ComponentScan 어노테이션을 통해 해결한다.

 

한 줄 정리

- 모델을 테스트하는 것과는 다르게 컨트롤러는 스프링 컨텍스트와 직접적인 관계를 갖기 때문에 스프링 컨텍스트를 로드하지 않는 Mockito, Junit 만으로는 부족하다.

- Mockito, Junit을 활용해 단위 테스트를 진행하고 실질적인 모의 HTTP 요청을 받기 위해 MockMvc를 활용한다.

- @SpringBootTest를 이용하면 모든 bean이 컨텍스트에 로드되기 때문에 모델단을 테스트하듯이 테스트 클래스를 구성할 수 있지만 테스트가 무거워진다.

- 가벼운 테스트를 위해 @WebMvcTest를 이용해 컨텍스트에 로드할 bean을 지정할 수 있다.

- @WebMvcTest를 통해 지정한 bean이 갖는 의존성은 @MockBean으로 등록해야 한다.(@MockBean은 @Mock과 다르게 스프링에서 제공하는 어노테이션이며 컨텍스트 위에 목객체를 로드하기 때문)

- 컨트롤러는 모델 단의 테스트와 다르게 서블릿과 연관된 부분이 많아 컨텍스트를 로드하고 테스트한다.

- 컨트롤러 테스트는 컨텍스트를 올리고 진행하기 때문에 security filter, config 등을 어디까지 컨텍스트에 올릴지 결정해야 한다.

- @AutoConfigureMockMvc 어노테이션을 통해 filter, 웹 드라이버 등을 지정해 줄 수 있다.

- filter를 지정하지 않고 컨트롤러를 테스트하고자 한다면 @AutoConfigureMockMvc(addFilters = false)를 사용하고 테스트 클래스에 직접 @Configuration을 지정한다.

- 서비스단은 Mockito, BDDMockito를 활용해 모킹한다.

 

 

 

 

Reference

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/web/servlet/MockMvc.html

https://spring.io/projects/spring-restdocs

- https://highright96.tistory.com/20