MockMVC + Junit으로 컨트롤러 테스트코드 만들기
API 서버를 만들다 보면 테스트를 주로 PostMan을 이용해 HTTP를 생성해서 컨트롤러의 동작을 테스트하거나 웹 브라우저를 켜서 테스트한다.
본 테스트 시간보다 웹 어플리케이션을 실행하고 종료하는 시간이 더 오래걸리는 상황이 발생한다.
또한, 매번 HTTP body에 데이터를 작성하는게 매우 비 효율적이다.
PostMan은 다음과 같은 문제점이 있다.
- 개발자의 수동 테스트
- 코드를 수정한 후에 서버를 재시작하고 테스트하기
이런 문제를 해결하기 위해 다음과 같은 방법을 사용할 수 있다.
- JUnit 사용 + MockMVC 사용
그 동안 웹 애플리케이션을 작성한 후, 해당 웹 애플리케이션을 Tomcat이라는 이름의 WAS(Web Application Server)에 배포(deploy)하여 실행을 하였다.
브라우저의 요청은 WAS에게 전달되는 것이고 응답도 WAS에게서 받게 된다.
WAS는 요청을 받은 후, 해당 요청을 처리하는 웹 어플리케이션을 실행한다.
즉, Web API를 테스트한다는 것은 WAS를 실행해야만 된다는 문제가 있다.
WAS 실행없이 테스트 코드로 Request & Response를 테스트 할순없을까?
이런 문제를 해결하기 위해서 스프링 3.2부터 MockMVC가 추가되었다.
MockMVC는 WAS와 같은 역할
을 수행한다.
요청을 받고 응답을 받는 WAS와 같은 역할을 수행하면서 작성한 웹 애플리케이션을 실행한다.
즉,테스트 환경에서 Request & Response를 완벽히 재현
할수있다.
WAS는 실행 시 많은 작업을 수행한다.
반면, MockMVC는 웹 어플리케이션을 실행하기 위한 최소한의 기능만을 가지고 있다.
그렇기 때문에 MockMVC를 이용한 웹 어플리케이션 실행은 상당히 빠르다.
MockMVC를 이용하면 다음과 같은 테스트를 수행할 수 있다.
MockMvc 객체란?
MockMvc는 서블릿 컨테이너의 구동 없이, 시뮬레이션된 MVC 환경에 모의 HTTP 서블릿 요청을 전송하는 기능을 제공하는 유틸리티 클래스다.
다음과 같이 MockMvc가 제공하는 메소드를 이용해서 스프링 웹 어플리케이션 프로젝트의 Controller를 테스트하는 코드가 있다.
이 코드를 살펴보며 MockMvc가 제공하는 메소드를 알아보자.
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@AutoConfigureMockMvc
class LoginControllerTest {
@Autowired
private MockMvc mockMvc; //MockMvc
@Autowired
private ObjectMapper objectMapper; //for json body 데이터 생성
@Test
void login() throws Exception {
String bodyContent = objectMapper.writeValueAsString(new LoginForm("startUser", "0602")); //body 데이터
mockMvc.perform(post("/login")
.content(bodyContent)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(print());
}
}
perform()
MockMvc가 제공하는 perform() 메소드를 사용하면 브라우저에서 서버에 URL 요청을 하듯 컨트롤러를 실행시킬 수 있다. perform() 메소드는 RequestBuilder 객체를 인자로 받고, 이는 MockMvcRequestBuilders의 정적 메소드를 이용해서 생성한다.
MvckMvcRequestBuilders
MvckMvcRequestBuilders의 메소드들은 GET, POST, PUT, DELETE 요청 방식과 매핑되는 get(), post(), put(), delete() 메소드를 제공한다. 이 메소드들은 MockHttpServletRequestBuilder 객체를 리턴하고,
HTTP 요청 관련 정보(파라미터, 헤더, 쿠키 등)를 설정할 수 있다.
MockHttpServletRequestBuilder의 메소드는 MockHttpServletRequestBuilder 객체를 다시 리턴하여 메시지 체인을 구성하여 복잡한 요청을 설정할 수도 있다.
andExpect()
perform() 메소드를 이용하여 요청을 전송하면, 그 결과로 ResultActions 객체를 리턴하는데 이 객체는 응답 결과를 검증할 수 있는 andExpect() 메소드를 제공한다.
andExpect()가 요구하는 ResultMatcher는 MockMvcResultMatchers에 정의된 정적 메소드를 통해 생성할 수 있다.
컨트롤러가 어떤 결과를 전송했는지, 서버의 응답 결과를 검증하는 MockMvcResultMatcher 객체는 다음과 같은 메소드를 제공한다.
MockMvcResultMatcher 객체가 제공하는 메소드
응답 상태 코드 검증
isOk() | 응답 상태 코드가 정상적인 처리(200)인지 확인 |
---|---|
isNotFount() | 응답 상태 코드가 404 Not Found인지 확인 |
isMethodNotAllowed() | 응답 상태 코드가 메소드 불일치(405)인지 확인 |
isInternalServerError() | 응답 상태 코드가 예외발생(500)인지 확인 |
is(int status) | 몇 번 응답 상태 코드가 설정되었는지 확인 ex) is(200), is(404) |
뷰/리다이렉트 검증
컨트롤러가 리턴하는 뷰를 검증할 때는 view() 메소드를 사용한다. andeExpect(view().name("user")) 코드는 컨트롤러가 리턴한 뷰 이름이 "hello"인지 검증한다.
만약 요청 처리 결과가 리다이렉트 응답이면 redirectedUrl() 메소드를 사용한다. andExpect(redirectedUrl("/user")) 코드는 '/user' 화면으로 리다이렉트했는지 검증한다.
모델 정보 검증
컨트롤러에서 저장한 모델의 정보를 검증하고 싶다면 MockMvcResultMatchers.model() 메소드를 사용한다.
attributeExists(String name) : name에 해당하는 데이터가 Model에 포함되어 있는지 검증
attribute(String name, Object value) : name에 해당하는 데이터가 value 객체인지 검증
요청/응답 전체 메시지 확인하기
생성된 요청과 응답 메시지를 모두 확인해보고 싶다면, perform 메소드가 리턴하는 ResultAction의 andDo(ResultHandler handler) 메소드를 사용한다.
MockMvcResultHandlers.print() 메소드는 ResultHandler를 구현한 ConsolerPrinting ResultHandler 객체를 리턴하여, ConsolePrintingResultHandler를 andDo() 메소드 인자로 넘겨주면 콘솔에 요청/응답과 관련된 정보를 모두 출력한다.
Reference
Spring test mvc 발표자료 (slideshare.net)
[Junit] MockMvc 객체란? (tistory.com)
'개발자 준비 > TDD' 카테고리의 다른 글
Mockito 알아보기 (0) | 2022.11.08 |
---|---|
어떻게 하면 테스트 주도 개발을 잘할까? (0) | 2022.11.08 |