본문 바로가기

Spring/Test

[Springboot] MockMvc를 이용해서 테스트하기

 

 

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