본문 바로가기
  • 실행력이 모든걸 결정한다
Spring Series/Spring Framework

[Spring] MockMvc 테스트

by 김코더 김주역 2022. 12. 20.
반응형

1. MockMvc란?

- 애플리케이션 서버를 구동하지 않고도 Spring MVC 동작을 재현할 수 있는 모의 객체다.

 

MockMvc 공식 문서

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/web/servlet/MockMvc.html

 

MockMvc (Spring Framework 6.0.3 API)

Perform a request and return a type that allows chaining further actions, such as asserting expectations, on the result.

docs.spring.io

 

 

 

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

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/web/servlet/MockMvc.html

 

MockMvc (Spring Framework 6.0.3 API)

Perform a request and return a type that allows chaining further actions, such as asserting expectations, on the result.

docs.spring.io

perform()

- 요청을 전송하는 역할을 한다.

- 인자는 RequestBuilders 타입 객체다. RequestBuilders의 구현체에 요청 정보를 담으면 되며, 구현체로는 MockMvcRequestBuilders를 사용하면 된다. 3)를 참고하자.

- 요청 결과로 ResultActions 객체를 반환받는다. 2)를 참고하자.

 

 

2) org.springframework.test.web.servlet.ResultActions;

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/web/servlet/ResultActions.html

 

ResultActions (Spring Framework 6.0.3 API)

Perform multiple expectations, with the guarantee that all expectations will be asserted even if one or more expectations fail with an exception.

docs.spring.io

(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.*;

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/web/servlet/request/MockMvcRequestBuilders.html

 

MockMvcRequestBuilders (Spring Framework 6.0.3 API)

Alternative factory method that allows for custom HTTP verbs (e.g.

docs.spring.io

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할 필요가 없다.

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.html

 

MockHttpServletRequestBuilder (Spring Framework 6.0.3 API)

contentType Set the 'Content-Type' header of the request as a raw String value, possibly not even well-formed (for testing purposes). Parameters: contentType - the content type Since: 4.1.2

docs.spring.io

(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

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/web/servlet/result/MockMvcResultMatchers.html

 

MockMvcResultMatchers (Spring Framework 6.0.3 API)

content Access to response body assertions.

docs.spring.io

(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://gom20.tistory.com/123

https://wedul.site/131

https://kth990303.tistory.com/140

 

 

반응형

댓글