Mock
- Mock이라는 단어를 사전에서 찾아보면 '테스트를 위해 만든 모형'을 의미한다.
- 테스트를 위해 실제 객체와 비슷한 모의 객체를 만드는 것을 모킹(Mocking)이라고 하며, 모킹한 객체를 메모리에서 얻어내는 과정을 목업(Mock-up)이라고 한다.
- 객체를 테스트하기 위해서는 당연히 테스트 대상 객체가 메모리에 있어야 한다.하지만 생성하는 데 복잡한 절차가 필요하거나 많은 시간이 소요되는 객체는 자주 테스트하기 어렵다. 또는 웹 애플리케이션의 컨트롤러처럼 WAS나 다른 소프트웨어의 도움이 반드시 필요한 객체도 있을 수 있다. 이런 복잡한 객체는 당연히 테스트 과정도 복잡하고 어려울 수 밖에 없다.
- 따라서 테스트 하려는 실제 객체와 비슷한 가짜 객체를 만들어서 테스트에 필요한 기능만 가지도록 모킹을 하면 테스트가 쉬워진다.
- 테스트하려는 객체가 복잡한 의존성을 가지고 있을 때, 모킹한 객체를 이용하면, 의존성을 단절시킬 수 있어서 쉽게 테스트할 수 있다.
- 웹 애플리케이션에서 컨트롤러를 테스트할 때, 서블릿 컨테이너를 모킹하기 위해서는 @WebMvcTest를 사용하거나 @AutoConfigureMockMvc를 사용하면 된다.
- 웹 환경에서 컨트롤러를 테스트하려면 반드시 서블릿 컨테이너가 구동되고 DispatcherServlet 객체가 메모리에 올라가야 하지만, 서블릿 컨테이너를 모킹하면 실제 서블릿 컨테이너가 아닌 테스트용 모형 컨테이너를 사용하기 때문에 간단하게 컨트롤러를 테스트할 수 있다.
Mock 객체로 테스트하기
1) @WebMvcTest 사용하기
- 웹에서 테스트하기 힘든 컨트롤러를 테스트하는데 적합한다.
- 웹상에서 요청과 응답에 대해 테스트할 수 있을 뿐만 아니라 시큐리티 혹은 필터까지 자동으로 테스트하며 수동으로 추가/삭제까지 가능하다.
@WebMvcTest
- @Controller, @RestController가 설정된 클래스들을 찾아 메모리에 생성한다.
- @Service나 @Repository가 붙은 객체들은 테스트 대상이 아닌 것으로 처리되기 때문에 생성되지 않는다.
- @WebMvcTest가 설정된 테스트 케이스에서는 서블릿 컨테이너를 모킹한 MockMvc타입의 객체를 목업하여 컨트롤러에 대한 테스트코드를 작성할 수 있다.
- @WebMvcTest 어노테이션을 사용하면 MVC 관련 설정인 @Controller, @ControllerAdvice, @JsonComponent와 Filter, WebMvcConfigurer, HandlerMethodArgumentResolver만 로드되기 때문에, 실제 구동되는 애플리케이션과 똑같이 컨텍스트를 로드하는 @SpringBootTest 어노테이션보다 가볍게 테스트 할 수 있다.
private MockMvc mvc
- 웹 API를 테스트할 떄 사용한다.
- 스프링 MVC 테스트의 시작점이다.
- 이 클래스를 통해 HTTP GET, POST 등에 대한 API 테스트를 할 수 있다.
mockMvc.perfom(get("/hello"))
- MockMvc를 통해 /hello 주소로 HTTP GET을 요청한다.
- 체이닝이 지원되어 아래와 같이 여러 검증 기능을 이어서 선언할 수 있다.
.andExpect(status().isOK())
- mvc.perfom의 결과를 검증한다.
- HTTP Header의 Status를 검증한다.
- 우리가 흔히 알고 있는 200, 400, 500 등의 상태를 검증한다.
- 여기선 OK 즉, 200인지 아닌지를 검증한다.
.andExpect(content().string( ... ))
- mvc.perform의 결과를 검증한다.
- 응답 본문의 내용을 검증한다.
2) @AutoConfigureMockMvc 사용하기
- @AutoConfigureMockMvc는 @WebMvcTest와 비슷하게 사용할 수 있는 어노테이션이다.
- @SpringBootTest에는 웹 애플리케이션 테스트를 지원하는 webEnvironment 속성이 있다. 이 속성을 생략하면 기본값으로 WebEnvironment.MOCK이 설정되어 있는데, 이 설정에 의해서 서블릿 컨테이너가 모킹된다.
- @SpringBootTest(webEnvironment=WebEnvironment.MOCK) 설정으로 모킹한 객체를 의존성 주입받으려면 @AutoCOnfigureMockMvc를 클래스 위에 추가 해야한다.
@AutoConfigureMockMvc
- @WebMvcTest와 가장 큰 차이점은은 컨트롤러뿐만 아니라 테스트 대상이 아닌 @Service나 @Repository가 붙은 객체들도 모두 메모리에 올린다는 것이다.
- 간단하게 테스트하기 위해서는 @AutoConfigureMockMvc가 아닌 @WebMvcTest를 사용해야 한다.
- @WebMvcTest는 @SpringBootTest와 같이 사용될 수 없다. 왜냐하면 각자 서로의 MockMvc를 모킹하기 때문에 충돌이 발생하기 때문이다.
MockMvc 메소드 이해하기
perform()
- MockMvc가 제공하는 perform()메소드를 사용하면 브라우저에서 서버에 URL 요청을 하듯 컨트롤러를 실행시킬 수 있다.
- MockMvc의 perform() 메소드는 RequestBuilder 객체를 인자로 받는데, RequestBuilder 객체는 MockMvcRequestBuilder의 정적 메소드를 이용해서 생성한다.
- MockMvcRequestBuilder의 메소드들은 GET, POST, PUT, DELETE 요청 방식과 매핑되는 get(), post(), put(), delete() 메소드를 제공한다.
그리고 이 메소드들은 MockHttpServletRequestBuilder 객체를 리턴하는데, 이 객체에 브라우저가 HTTP 요청 프로토콜에 요청 관련 정보(파라미터, 헤더, 쿠키 등)를 설정하듯 다양한 정보들을 설정할 수 있다.
- 위 코드처럼 param() 메소드를 이용하면 '키=값'의 파라미터를 여러 개 전달할 수 있다.
- MockHttpServletRequestBuilder의 메소드는 MockHttpServletRequestBuilder 객체를 다시 리턴하기 때문에 메시지 체인을 구성하여 복잡한 요청을 설정할 수 있다.
andExpect()
- perform() 메서드를 이용하여 요청을 전송하면, 그 결과로 ResultActions 객체를 리턴하는데, ResultActions는 응답 결과를 검증할 수 있는 andExpect() 메서드를 제공한다.
- andExpect()가 요구하는 ResultMatcher 객체는 MockMvcResultMatchers에 정의된 정적메서드를 통해 생성할 수 있다.
- 컨트롤러의 동작을 테스트하기 위해서는 요청도 중요하지만 사실 컨트롤러가 어떤 결과를 전송했는지 검증하는 것이 가장 중요하다.
- 서버의 응답 결과는 MockMvcResultMatchers 객체의 메소드를 이용하여 검증할 수 있다.
(1) status() : 응답 상태 코드 검증
- MockMvcResultMatchers의 status() 메소드는 StatusResultMatchers 객체를 리턴하는데 이 객체를 이용하면 응답 상태 코드를 검증할 수 있다.
메소드 | 설명 |
isOK() | 응답 상태 코드가 정상 처리에 해당하는 200인지 확인한다. |
isNotFound() | 응답 상태 코드가 404 Not Found인지 확인한다. |
isMethodNotAllowed() | 응답 상태 코드가 메소드 불일치에 해당하는 405인지 확인한다. |
isInternalServerError() | 응답 상태 코드가 예외 발생에 해당하는 500인지 확인한다. |
is(int status) |
몇 번 응답 상태 코드가 설정되어 있는지 확인한다. 예) is(200), is(404), is(405), is(500) |
(2) view() : 뷰/리다이렉트 검증
- 컨트롤러가 리턴하는 뷰를 검증할 때는 view() 메소드를 사용한다.
- andExpect(view().name("hello")) 코드는 컨트롤러가 리턴한 뷰 이름이 "hello"인지 검증한다.
- 만약 요청 처리 결과가 리다이렉트 응답이라면 redirectedUrl() 메소드를 사용하면 된다.
- andExpect(redirectedUrl("/index")) 코드는 "/index" 화면으로 리다이렉트했는지를 검증한다.
(3) model() : 모델 정보 검증
- 컨트롤러에서 저장한 모델의 정보들을 검증하고 싶으면 MockMvcResultMatchers.model() 메소드를 사용한다.
메소드 | 설명 |
attributeExists(String name) | name에 해당하는 데이터가 Model에 포함되어있는지 검증한다. |
attribute(String name, Object value) | name에 해당하는 데이터가 value 객체인지 검증한다. |
(4) andDo() : 요청/응답 전체 메시지 확인하기
- MockMvc를 이용해서 테스트를 진행할 떄, 실제로 생성된 요청과 응답 메시지를 모두 확인해보고 싶은 경우에는 perform() 메소드가 리턴하는 ResultActions의 andDo(ResultHandler handler) 메소드를 사용하면 된다.
- MockMvcResultHandlers.print() 메소드는 ResultHandler를 구현한 ConsolePrintingResultHandler 객체를 리턴한다.
- ConsolePrintingResultHandler를 andDo() 메소드 인자로 넘겨주면 콘솔에 요청/응답과 관련된 정보를 모두 출력한다.
내장 Tomcat으로 테스트하기
- 테스트 케이스에서 정상적으로 서블릿 컨테이너를 구동하고 테스트 결과를 확인하고 싶으면 @SpringBootTest에서 webEnvironment 속성값을 RANDOM_PORT나 DEFINED_PORT로 변경하면 된다.
webEnvironment
- 애플리케이션이 실행될 때의 웹 환경을 설정할 수 있다.
- 기본값은 Mock 서블릿을 로드하여 구동되며 위 코드에서는 랜덤 포트값을 주어 구동시킨다.
상수 | 의미 |
MOCK |
모킹된 서블릿 컨테이너를 제공하기 때문에 내장 톰캣이 구동되지 않는다. @AutoConfigureMockMvc 어노테이션을 사용하여 MockMvc 객체를 주입 받아 테스트에 사용할 수 있다. |
RANDOM_PORT |
랜덤한 포트로 내장 톰캣을 구동하여 서블릿 컨테이너를 초기화한다. 정상적인 서블릿 테스트가 가능하다.
|
DEFINED_PORT | RANDOM_PORT와 동일하지만, application.properties 파일에 설정된 서버 포트를 사용한다. |
NONE | 서블릿 기반의 환경 자체를 구성하지 않는다., |
TestRestTemplate
- webEnvironment 속성값을 WebEnvironment.RANDOM_PORT로 지정하면 더 이상 서블릿 컨테이너를 모킹하지 않기 때문에 MockMvc 객체를 목업할 수 없다. 따라서 MockMvc 객체 대신 실제 컨트롤러를 실행해줄 TestRestTemplate 객체를 주입해서 컨트롤러를 요청해야 한다.
- TestRestTemplate 객체를 이용하면 특정 URL로 서버에 요청을 전달할 수 있으며 응답 결과도 검증할 수 있다.
- getForObject() 메소드의 첫 번째 인자로 서버에 요청할 URL을 지정했고, 두 번째로 응답 결과의 타입 클래스를 지정했다.
- assertEquals() 메소드를 이용하여 응답 결과 메시지를 확인하였다.
- assertThat()라는 테스트 검증 라이브러리의 검증 메소드를 사용하여 검증하고 싶은 대상을 메소드로 인자로 받아서 결과를 확인하였다.
- assertThat()는 메소드 체이닝이 지원되어 isEqualTo(동등 비교 메소드)와 같이 메소드를 이어서 사용할 수 있다.
'Java > SpringBoot' 카테고리의 다른 글
[SpringBoot] OAuth2.0으로 소셜로그인 기능 구현하기(1) - 서비스 등록하기 (0) | 2020.03.29 |
---|---|
[SpringBoot] 그레이들 프로젝트를 스프링부트 프로젝트로 변경하기 (0) | 2020.03.27 |
[SpringBoot] Test(1) 스프링 부트에서 테스트하기(@SpringBootTest) (0) | 2020.03.21 |
[SpringBoot] Starter로 의존성 관리하기 (0) | 2020.03.04 |
[SpringBoot] JPA(Java Persistence) 개념 정리 (0) | 2020.02.25 |