1. 용어 정리
- 목(Mock): 테스트를 위해서 만든 모형
- 모킹(Mocking): 테스트를 위해 실제 객체와 비슷한 모의 객체를 만드는 것
- 목업(Mock-Up): 모킹한 객체를 메모리에서 얻어내는 과정
2. 서블릿 컨테이너 모킹
Controller를 테스트할 때 서블릿 컨테이너를 모킹을 위해 @WebMvcTest 또는 @AutoConfigureMockMvc 사용함
스프링 3.2부터 스프링 프레임워크는 스프링 MVC를 모킹하여 웹 애플리케이션을 테스트하는 아주 유용한 기능을 제공한다. 이 기능으로 실제 서블릿 컨테이너에서 컨트롤러를 실행하지 않고도 컨트롤러에 HTTP 요청을 할 수 있다. 스프링 Mock MVC 프레임워크는 애플리케이션을 마치 서블릿 컨테이너에서 실행하는 것처럼 스프링 MVC를 흉내 내지만 실제 컨테이너에서 실행하지는 않는다. 테스트에서 Mock MVC를 설정하려면 MockMvcBuilders를 사용한다. 이 클래스는 정적 메서드 두 개를 제공한다. • webAppContextSetup() : 짐작건대 구성된 컨트롤러 한 개 이상을 포함하는 스프링 애플리케이션 컨텍스트(WebApplicationContext)를 사용하여 Mock MVC를 만든다. • standaloneSetup() : 수동으로 생성하고 구성한 컨트롤러 한 개 이상을 서비스할 Mock MVC를 만든다. 이 두 옵션의 가장 큰 차이는 standaloneSetup() 메서드는 테스트할 컨트롤러를 수동으로 초기화하고 주입하기를 기대하는 반면, webAppContextSetup() 메서드는 (스프링이 로드한) WebApplicationContext의 인스턴스로 작동한다는 점이다. standaloneSetup() 메서드는 한 컨트롤러에 집중하여 테스트하는 용도로만 사용한다는 점에서 유닛 테스트와 유사하다. 반면에 webAppContextSetup() 메서드는 스프링이 컨트롤러는 물론 의존성까지 로드하여 완전한 통합 테스트를 할 수 있게 한다. |
1) @WebMvcTest
- @Controller, @RestController가 붙은 객체를 메모리에 올림
- @Service, @Repository가 붙은 객체는 테스트 대상이 아니기 때문에 생성되지 않음
- @SpringBootTest와 같이 사용할 수 없음(각자 서로의 MockMvc를 모킹하기 때문에 충돌 발생)
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.web.servlet.MockMvc;
@WebMvcTest // @Controller, @RestController 메모리에 등록
public class BoardControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testHello() throws Exception {
mockMvc.perform(get("/hello").param("name", "sky"))
.andExpect(status().isOk())
.andExpect(content() .string("Hello : sky"))
.andDo(print());
}
}
2) @AutoConfigureMockMvc
1) 설명
- MockMvc 자동 구성을 위해 사용
- @Controller, @RestController 뿐만 아니라 @Service, @Repository가 붙은 객체를 메모리에 올림
- @SpringBootTest와 같이 사용할 수 있음
2) 코드 예시
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.web.servlet.MockMvc;
@SpringBootTest(webEnvironment = WebEnvironment.MOCK)
@AutoConfigureMockMvc // 컨트롤러 뿐만아니라 @Repository, @Service까지 메모리에 다 올린다.
public class BoardControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testHello() throws Exception {
mockMvc.perform(get("/hello").param("name", "sky"))
.andExpect(status().isOk())
.andExpect(content().string("Hello : sky"))
.andDo(print());
}
}
3) MockMvc 메서드
메서드 | 설명 |
perform() | - 마치 브라우저에서 서버에 URL을 요청하듯 컨트롤러를 실행시킬 수 있음 - MockMvcRequestBuilders의 정적 메서드를 이용해서 생성한 RequestBuilder 객체를 인자로 받음 (MockMvcRequestBuilders의 메서드들은 GET, POST, PUT, DELETE 요청과 매칭이되는 get(), post(), put(), delete() 메서드 제공하며 mockHttpServletRequestBuilder객체를 리턴함, 이 객체가 브라우저가 Http 요청 프로토콜에 요청 관련 정보(파라미터, 헤더, 쿠키 등)를 설정하 듯 다양한 정보를 설정할 수 있음) - perform() 메서드 요청 전송 시 그 결과로 ResultActions 객체 리턴(ResultActions는 응답 결과를 검증할 수 있는 andExpert()메서드 제공) |
andExpect() | - 서버의 응답 결과 검증 - andExpect()메서드가 요구하는 ResultMatcher는 MockMvcResultMatchers에 정의된 정적 메서드를 통해서 생성 - 서버의 응답 결과는 MockMvcResultMatchers 객체의 메서드를 이용해서 검증할 수 있음 |
andDo() |
- 요청/응답 메시지를 모두 확인해 보고 싶은 경우 perform() 메서드가 리턴하는 ResultActions의 andDo(ResultHandler handler) 메서드 사용
- MockMvcResultHandlers.print() 메서드는 ResultHandler를 구현한 ConsolePrintingResultHandler 객체 리턴 - ConsolePrintintResultHandler를 andDo() 메서드의 인자로 넘겨주면 콘솔에 요청/응답과 관련된 모든 정보를 출력 |
andExpect(MockMvcResultMatchers.satus().*)
MockMvcResultMatchers의 status() 메서드는 StatusResultMatchers 객체를 리턴하는데, 이 객체를 이용하여 응답 상태 코드 검증
메서드 | 설명 |
isOk() | 정상 처리(200) 확인 |
isNotFound() | Not Found(404) 확인 |
isMethodNotAllowed() | 메서드 불일치(405) 확인 |
isInternalServerError() | 예외 발생(500) 확인 |
is(int status) | 몇 번 응답 상태 코드가 설정되어 있는 지 확인 |
3. 내장 톰캣으로 테스트하기
- 정상적으로 서블릿 컨테이너를 구동하고 테스트 결과를 확인하고 싶으면 @SpringBootTest에서 webEnvironment 속성값을 RANDOM_PORT나 DEFINED_PORT로 변경하여 테스트
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc // 컨트롤러 뿐만아니라 @Repository, @Service까지 메모리에 다 올린다.
public class BoardControllerTest {
@Autowired
private TestRestTemplate restTemplate;
// 단순 문자열 리턴 테스트
@Test
public void testGetBoard() throws Exception {
String result = restTemplate.getForObject("/hello?name=sky", String.class);
assertEquals("Hello: sky", result);
}
// VO객체 리턴 테스트
@Test
public void testGetBoardVO() throws Exception {
BoardVO board = restTemplate.getForObject("/getBoard", BoardVO.class);
assertEquals("sky", board.getWriter());
}
}
'Spring > Test' 카테고리의 다른 글
[Springboot] SpringBoot 테스트 @SpringBootTest (0) | 2022.12.19 |
---|---|
AssertJ (0) | 2022.12.01 |
TDD, 대역을 이용한 테스트 (0) | 2022.11.30 |
TDD, 테스트 코드의 구성 (0) | 2022.11.30 |
TDD 테스트 주도 개발, 기능 명세와 설계 (0) | 2022.11.27 |