1. MockMvc란?
- 애플리케이션 서버를 구동하지 않고도 Spring MVC 동작을 재현할 수 있는 모의 객체다.
MockMvc 공식 문서
2. Dependencies
- spring-boot-starter-test에는 MockMvc가 포함되어 있다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
3. MockMvc를 사용하기 위한 방법
1) @SpringBootTest + @AutoConfigureMockMvc
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class DemoApplicationTests {
@Autowired
MockMvc mockMvc;
...
}
- @SpringBootTest는 통합테스트시 사용되는 어노테이션이다. 그만큼 모든 bean을 로드하기 때문에 테스트 구동 시간이 오래 걸린다.
- @AutoConfigureMockMvc : Mock 테스트시 필요한 의존성을 제공하는 어노테이션이다. 컨트롤러 외에도 @Service, @Repository가 붙은 객체들까지 모두 메모리에 올린다.
2) @WebMvcTest
@RunWith(SpringRunner.class)
@WebMvcTest
public class DemoApplicationTests {
@Autowired
MockMvc mockMvc;
...
}
- @WebMvcTest : MVC 부분만 따로 테스트하고 싶을 때 사용된다. @WebMvcTest(HomeController.class)와 같이 테스트 대상 컨트롤러를 지정할 수도 있다.
참고로, @SpringBootTest와 @WebMvcTest를 같이 사용하면 충돌 위험성이 있기 때문에 같이 사용하지 않도록 하자.
+추가) 직접 생성
- 반복되는 설정을 미리 설정할 수도 있다.
@Before
public void before() {
this.mockMvc = MockMvcBuilders.standaloneSetup(HomeController.class)
.alwaysExpect(MockMvcResultMatchers.status().isOk()) // 모든 예상 응답 status를 OK(200)로 설정
.build();
}
4. MockMvc 테스트에 사용되는 주요 클래스와 메소드
예시 코드는 [5. 샘플 코드]에서 다룰 것이니, 테스트를 위한 메소드는 어떤 것들이 있는지 정도만 훑어보자.
1) org.springframework.test.web.servlet.MockMvc
perform()
- 요청을 전송하는 역할을 한다.
- 인자는 RequestBuilders 타입 객체다. RequestBuilders의 구현체에 요청 정보를 담으면 되며, 구현체로는 MockMvcRequestBuilders를 사용하면 된다. 3)를 참고하자.
- 요청 결과로 ResultActions 객체를 반환받는다. 2)를 참고하자.
2) org.springframework.test.web.servlet.ResultActions;
(1) andExpect()
- 응답을 검증하는 역할을 한다.
- 인자는 ResultMatcher 타입 객체다. ResultMatcher의 구현체에 검증 정보를 담으면 되며, 구현체로는 MockMvcResultMatchers를 사용하면 된다. 5)를 참고하자.
- ResultActions 객체를 다시 반환하여 체인을 구성한다.
(2) andDo()
- 일반적인 동작을 수행한다.
- 인자는 ResultHandler 타입 객체다. ResultHandler은 요청 결과를 출력하는 역할을 수행한다. 구현체로는 MockMvcResultHandlers를 사용하면 되며, 그 안에 log()와 print() 메소드가 존재한다.
- 주로 다음과 같이 전체 요청/응답 메시지 정보를 확인한다.
andDo(print())
- ResultActions 객체를 다시 반환하여 체인을 구성한다.
3) org.springframework.test.web.servlet.request.MockMvcRequestBuilders
다음과 같이 정적 메소드로 사용한다.
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
get() / post() / put() / delete()
- HTTP 메소드에 해당하는 메소드다.
- 인자로는 경로를 보내주면 된다.
- 추가적인 요청 정보(요청 파라미터, Accept 헤더, 인코딩, 요청 바디, Content-Type, 쿠키, 세션 등)를 부여하기 위한 MockHttpServletRequestBuilder 타입 객체를 반환한다. MockHttpServletRequestBuilder의 메소드는 MockHttpServletRequestBuilder 객체를 다시 리턴하여 체인을 구성한다. 4)를 참고하자.
4) org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder
3)에서 설명한 MockMvcRequestBuilders를 이미 import static으로 불러오고 있다면, MockHttpServletRequestBuilder를 추가로 import할 필요가 없다.
(1) params() : 요청 파라미터 부여
(2) accept() : Accept 헤더 부여
(3) characterEncoding() : 요청의 인코딩 정보 부여
(4) content() : 요청 바디 부여 (주로 post 요청 시 @RequestBody로 데이터를 전송하기 위해 사용함)
(5) contentType() : Content-Type 부여
(6) cookie() : 쿠키 부여
(7) session() : 세션 부여
※ sessionAttr() : 세션 애트리뷰트 부여
(8) header() : 헤더 부여
5) org.springframework.test.web.servlet.result.MockMvcResultMatchers
(1) status() : 상태 코드를 검증 - 예) status().isOk()
(2) view() : 반환하는 뷰의 이름을 검증 - 예) view().name("hello")
(3) redirectedUrl() : 리다이렉트 경로를 검증 - 예) redirect("/hello")
(4) model() : 컨트롤러에서 저장한 모델의 정보를 검증 - 예) model().attribute("name", "Spring")
(5) content() : 응답 데이터 검증 - 예) content().string("Hello, Guys")
(6) jsonPath() : json 포맷의 응답을 검증한다. json path가 깊어질 때마다 .을 추가하면 되고, json 배열의 경우 [인덱스]를 표기하면 된다.
.andExpect(jsonPath("$.data").isArray())
.andExpect(jsonPath("$.data[0].name").value("Jooyeok"))
.andExpect(jsonPath("$.data[0].title").value("What is MockMvc?"))
※ jsonPath()는 JsonPathResultMatchers 객체를 반환한다. JsonPathResultMatchers 클래스에는 isArray(), exists(), isNumber(), isString(), value() 메소드 등이 있다. (https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/web/servlet/result/JsonPathResultMatchers.html)
5. 샘플 코드
@Test
public void Home_테스트() throws Exception {
mockMvc.perform(get("/home"))
.andExpect(status().isOk()) // 200(Ok) 응답 확인
.andDo(print());
}
@Test
public void 존재하지_않는_페이지_테스트() throws Exception {
mockMvc.perform(get("/fakepage")) // "/fakepage" 요청을 처리하는 컨트롤러가 존재하지 않음
.andExpect(status().isNotFound()) // 404(Not Found) 응답 확인
.andDo(print());
}
@Test
public void Unit1_테스트() throws Exception {
int unit=1;
int question1 = 5;
int question2 = 2;
String question3 = "SELECT NAME, POSITION, SALARY FROM EMPLOYEE";
String body = "{\"unit\":"+unit+", \"question1\": "+question1+", \"question2\": "+question2+", \"question3\": \""+question3+"\"}";
mockMvc.perform(post("/test/grading")
.content(body) // request body를 POST 요청으로 전송
.contentType(MediaType.APPLICATION_JSON)) // json 포맷의 요청임을 명시
.andExpect(status().isOk())
.andExpect(jsonPath("$.questionList[0].isCorrect").value(true))
.andExpect(jsonPath("$.questionList[1].isCorrect").value(true))
.andExpect(jsonPath("$.questionList[2].isCorrect").value(true))
.andDo(print());
}
추후 포스팅 예고
Service나 Repository의 경우에는 @MockBean으로 주입받아 테스트를 진행하면 된다. @MockBean는 단위 테스트에만 집중할 수 있도록 도와주는 어노테이션으로, 객체의 호출과 결과를 임의로 조작하여 테스트를 지원할 수 있다. 나중에 @MockBean에 대한 포스팅도 올릴 계획이다.
▣ 참고 자료
https://shinsunyoung.tistory.com/52
https://kth990303.tistory.com/140
'Spring Series > Spring Framework' 카테고리의 다른 글
[Spring] ModelMapper (0) | 2022.12.15 |
---|---|
[Spring] @Enable 어노테이션을 이용한 설정 모듈화 (0) | 2022.10.28 |
[Spring] 캐시 추상화 (0) | 2022.10.27 |
[Spring] Spring의 Task와 Scheduling (0) | 2022.10.25 |
[Spring] 리모팅과 EJB (0) | 2022.10.25 |
댓글