MockMvc에 대해서 한번 공부해보려고 합니다.
MockMvc는 웹 어플리케이션을 애플리케이션 서버에 배포하지 않고 테스트용 MVC환경을 만들어 요청 및 전송, 응답기능을 제공해주는 유틸리티 클래스라고 합니다.
(말 그대로 진짜 MockMvc 인 것입니다.)
즉, 실제 객체와 비슷하지만 테스트에 필요한 기능만 가지는 가짜 객체를 만들어서 애플리케이션 서버에 배포하지 않고도(실제 서블릿 컨테이너를 사용하지 않고) 스프링 MVC 동작을 재현할 수 있는 클래스를 의미합니다.
MockMvc 필요성이기도 하지만 Mock 필요성에 대해 말을 하자면
객체를 테스트하려면 테스트 대상 객체가 메모리에 있어야 하는데 생성하는 데 복잡한 절차가 필요하거나 많은 시간 이 소요되는 객체가(ex. 서비스레이어) 있을 수 있고, 웹 어플리케이션의 Controller처럼 WAS나 다른 소프트웨어의 도움이 반드시 필요한 객체도 있을 수 있습니다.
-> 즉 실제 객체를 테스트 하려면 준비 과정이라던지 라이브러리 주입 등 비용과 시간이 많이 들게 됩니다.
이때 Mock 은 이러한 문제를 해결해 더욱 빠른 테스트를 진행할 수 있습니다.
@WebMvcTest
또는 @AutoConfigureMockMvc
를 사용합니다.@WebMvcTest
@AutoConfigureMockMvc
차이점
@WebMvcTest
@Controller
, @ControllerAdvice
등을 사용 가능@Service
, @Component
, @Repository
등은 사용 불가 @AutoConfigureMockMvc
@Service
나 @Repository
붙은 객체들도 모두 메모리에 올립니다.@WebMvcTest
보다@AutoConfigureMockMvc
+ @SpringBootTest
를 고려해야 합니다.따라서 정말 심플하게 테스트를 수행할 거면 @WebMvcTest
를
세밀하게 고려할거면 @AutoConfigureMockMvc
를 사용하면 될 것 같습니다.
@SpringBootTest
@Transactional
@AutoConfigureMockMvc
class CategoryControllerTest {
@Autowired
private MockMvc mockMvc;
private final String baseUrl = "/api/v1/category";
GET /api/v1/category 호출 시 반환되는 Json Response
{
"data" : [
{
"id" : 23,
"name" : "음식"
}
],
"code" : "FIND_ALL",
"message" : "카테고리 조회 성공"
}
@Test
public void 전체조회() throws Exception {
// when then
mockMvc.perform(get(baseUrl)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()) // status 200
.andExpect(jsonPath("$.data").isArray()) // 배열인지
.andExpect(jsonPath("$.data").isNotEmpty()) // 배열이 안비었는지
.andExpect(jsonPath("$.code").value("FIND_ALL")) // 값 비교
.andExpect(jsonPath("$.message").value("카테고리 조회 성공")) // 값 비교
.andDo(print()); // 출력
}
mockMvc.perform(get(baseUrl))
.andExpect(jsonPath("$.data.id").exists())
mockMvc.perform(get(baseUrl))
.andExpect(jsonPath("$.data").doesNotExist()); // null 인지 판별
등등 다양한 Matchers 가 존재함으로 필요한 Matcher가 있으면 따로 찾아서 적용하면 될 것 같습니다.
mockMvc.perform(get("test")
.param("query", "부대찌개")
.cookie("쿠키 값")
.header("헤더 값")
.contentType(MediaType.APPLICATION.JSON)
.content("json으로"));
Request, Response 설정 하는 부분이 () 이거 떄문에 헷갈릴수 있는데
통신 메소드 뒤에 바로 체인걸면 Request 설정이고
perform 메소드 뒤에 체인걸면 Reponse 설정 입니다.
mockMvc.perform(get("test"))
.param("query", "부대찌개")
.cooke("쿠키 값")
.header("헤더 값")
.contentType(MediaType.APPLICATION.JSON)
.content("json으로")
.andExpect(status().isOk())
.andExpect(content().string("expect json값"))
.andExpect(redirectedUrl("/foodList?name=부대찌개"));
// .andExpect(view().string("뷰이름"));
MockMvc를 쓰다가 Response 에서 한글이 깨지는 경우가 발생을 하였다.
이때 테스트 코드 자체는 이상이 없습니다. 단순 출력에서 깨지는 거라서
MockMvc에 필터를 추가를 해줘서 한글 인코딩이 가능하게 바꿔보았습니다.
@SpringBootTest
@Transactional
@AutoConfigureMockMvc
class CategoryControllerTest {
private MockMvc mockMvc;
@Autowired
private WebApplicationContext ctx;
private final String baseUrl = "/api/v1/category";
@BeforeEach
private void setup() {
// Response Content 에서 출력시 한글 깨짐 방지
// 이 방법은 WebApplicationContext를 주입 받아야하므로 @SpringBootTest에서만 사용가능합니다.
// 혹은 webAppContestSetup 대신에 standAloneSetup 으로 컨트롤러를 지정할수도있음
this.mockMvc = MockMvcBuilders.webAppContextSetup(ctx)
.addFilters(new CharacterEncodingFilter("UTF-8", true)) // 필터 추가
.alwaysDo(print())
.build();
}
이렇게 해주면 제대로 인코딩이 되어 한글이 잘 출력이 됩니다.
여기서 MockMvcBuilders.webAppContextSetup(ctx)
가 있고
MockMvcBuilders.standaloneSetup(controller)
가 있는데 살펴보자면
webAppContextSetup
@SpringBootTest
또는 @WebAppConfiguration
가 필요합니다.standaloneSetup
MockMvc를 쓰면서 코틀린 DSL 을 너무나도 쓰고싶다고 느꼈고 ㅋㅋㅋㅋㅋㅋ
TDD를 정말 제대로 하려면 진짜 많이 알아야한다고 느꼈습니다.
TDD도 공부해야하고
JVM도 해야하고
JPA도 해야하고
Spring도 해야하고
Gradle도 해야하고
Java 랑 Kotlin도 공부해야되는데...
알고리즘, 디자인 패턴 은 또 언제하지 ㅠㅠㅠㅠ