MockMvc Test

MockMvc를 이용한 컨트롤러 테스트

Spring과 연동하면 Spring Bean은 모두 테스트가 가능하다. 하지만 컨트롤러의 경우 요청과 존재해야 하며, 주입할 수 있는 객체가 아니기 때문에 테스트가 불가하다. Spring Test에서는 MockMvc 객체를 이용하여 가상의 요청을 만들고 그에 따른 실행 결과를 테스트 할 수 있다.

테스트 컨트롤러를 생성한다.

src/main/java : com.hakademy.spring11.controller.TestController

package com.hakademy.spring11.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class TestController {
	
	@GetMapping("/test1")
	@ResponseBody
	public String test1() {
		return "test1 complete";
	}
	
}

이 컨트롤러가 정상적으로 작동하는지 테스트 하는 방법은 2가지가 있다.

  • 서버에 프로젝트를 올려 구동한 뒤 브라우저를 이용하여 확인한다.

  • MockMvc를 이용하여 테스트를 수행한다.

MockMvc를 이용하는 테스트 케이스를 생성한다.

src/test/java : com.hakademy.spring11.Test

package com.hakademy.spring11;

public class Test06 {

}

다른 Spring과 연동 없이 단독으로 TestController 하나만 테스트 하기 위해 StandAlone 형태로 MockMvc 인스턴스를 생성한다.

MockMvc mockMvc;

@Before
public void prepare() {
	mockMvc = MockMvcBuilders.standaloneSetup(new TestController()).build();
}

완성된 MockMvc 객체를 이용하여 테스트를 수행한다.

@Test
public void test() throws Exception {
	mockMvc.perform(get("/test1"))
					.andDo(print())
					.andExpect(status().is2xxSuccessful())
					.andReturn();
}

테스트 수행 시 다음과 같은 결과를 확인할 수 있다.

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /test1
       Parameters = {}
          Headers = {}

Handler:
             Type = com.hakademy.spring11.controller.TestController
           Method = public java.lang.String com.hakademy.spring11.controller.TestController.test1()

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = {Content-Type=[text/plain;charset=ISO-8859-1], Content-Length=[14]}
     Content type = text/plain;charset=ISO-8859-1
             Body = test1 complete
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

MockMvc 명령 정리

MockMvc를 이용하여 테스트를 수행할 때 필요한 명령들은 다음과 같다.

  • .perform() : 요청을 수행하고 추가 조치를 수행할 수 있는 ResultActions 데이터 반환

  • .andDo() : 수행할 일반적인 행동을 설정합니다.

  • .andExpect() : 성공 상황을 설정(단정)합니다.

  • .andReturn() : 결과에 대한 정보들을 MvcResult 형태로 반환

perform

perform은 요청을 수행하기 위한 기본 명령이다. 내부에는 MockMvcRequestBuilders 클래스의 명령들을 사용하여 수행할 작업을 지정해줄 수 있다. MockMvcRequestBuilders는 static import로 생략이 가능하며 주요 명령으로는 다음과 같다.

  • get(url) : url에 대한 GET 방식의 요청을 수행

  • post(url) : url에 대한 POST 방식의 요청을 수행

  • put(url) : url에 대한 PUT 방식의 요청을 수행

  • delete(url) : url에 대한 DELETE 방식의 요청을 수행

  • patch(url) : url에 대한 PATCH 방식의 요청을 수행

요청 방식을 결정한 뒤에는 다음과 같이 추가 작업을 설정할 수 있다.

  • .param(name, value) : 요청 파라미터를 추가(name=value)

  • .accept(media) : accept 설정 수행

  • .characterEncoding(enc) : 요청 인코딩을 enc로 설정

  • .cookies(cks) : 요청에 쿠키를 추가

  • .contentType(type) : 요청의 MIME-TYPE을 type으로 설정

  • .requestAttr(name, value) : 요청에 Attribute를 추가

  • .session(mock) : 세션 추가

  • .sessionAttr(name, value) : 세션에 Attribute를 추가

  • .header(name, value) : 헤더 추가

  • .locale(locale) : 요청 언어 설정

andDo

andDo는 요청과 함께 수행할 작업을 지정할 때 사용한다. 사용할 수 있는 명령의 종류는 MockMvcResultHandlers에 정의되어 있으며 다음과 같다.

  • .print() : 지정된 대상에 메시지 출력

  • .log() : 로그 형태의 출력

andExpect

andExpect는 기대되는 상황에 대해서 정의할 때 사용한다. 복수개 설정이 가능하며, 상황에 맞지 않을 경우에는 테스트가 실패한다. 사용할 수 있는 명령의 종류는 MockMvcResultMatchers에 정의되어 있으며 다음과 같다.

  • .content() : Response Body를 검증할 때 사용

  • .cookie() : 쿠키를 검증할 때 사용

  • .flash() : Flash Attribute를 검증할 때 사용

  • .forwardedUrl(url) : forward된 URL이 url인지 검증할 때 사용

  • .forwardedUrlPattern(pattern) : forwared된 URL이 pattern 형태인지 검증할 때 사용

  • .handler() : 처리 핸들러를 검증할 때 사용

  • .header() : header를 검증할 때 사용

  • .redirectedUrl(url) : redirect된 URL이 url인지 검증할 때 사용

  • .redirectedUrlPattern(pattern) : redirect된 URL이 pattern 형태인지 검증할 때 사용

  • .request() : 요청을 검증할 때 사용

  • .status() : 상태를 검증할 때 사용

  • .view() : view를 검증할 때 사용

andReturn

andReturn은 테스트 수행 후 MvcResult 형태의 객체를 반환한다.

  • Request

  • Response

  • Exception

  • Interceptor

  • AsyncResult

등의 정보를 확인할 수 있다.

MockMvc를 이용한 WebApplication 테스트

Standalone 방식의 테스트로는 ViewResolver 등 연관된 기능들을 포함한 테스트를 구현하기 어렵다. 다음 매핑을 TestController에 추가한 뒤 테스트를 수행해서 원인을 찾아본다.

src/main/java : com.hakademy.spring11.TestController

@GetMapping("/test2")
public String test2() {
	return "test2";
}

@ResponseBody가 없기 때문에 반환값인 test2는 ViewResolver와 결합하여 /WEB-INF/views/test2.jsp로 인식되어야 한다. 테스트를 수행하여 정상적으로 처리되는지 확인해본다.

src/test/java : com.hakademy.spring11.Test07

package com.hakademy.spring11;

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.status;

import org.junit.Before;
import org.junit.Test;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import com.hakademy.spring11.controller.TestController;

public class Test07 {
	
	MockMvc mockMvc;
	
	@Before
	public void prepare() {
		mockMvc = MockMvcBuilders.standaloneSetup(new TestController()).build();
	}
	
	@Test
	public void test() throws Exception {
		mockMvc.perform(get("/test2"))
						.andDo(print())
						.andExpect(status().is2xxSuccessful())
						.andReturn();
	}
	
}

테스트를 수행하면 오류가 발생하며, 오류 로그는 다음과 같이 나온다.

javax.servlet.ServletException: Circular view path [test2]: would dispatch back to the current handler URL [/test2] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)

오류 메시지의 내용은 ViewResolver setup을 확인하라는 것이다. 즉, ViewResolver를 사용할 수 없다는 것인데, 이 때는 Spring의 환경과 연동한 테스트를 수행하여야 한다.

Spring과 연동한 테스트를 수행하고 싶을 경우 다음과 같이 테스트를 구성해야 한다.

src/test/java : com.hakademy.spring11.Test08

package com.hakademy.spring11;

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.status;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = {
	"file:src/main/webapp/WEB-INF/spring/root-context.xml",
	"file:src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml"
})
public class Test08 {
	
	@Autowired
	WebApplicationContext context;
	
	MockMvc mockMvc;
	
	@Before
	public void prepare() {
		mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
	}
	
	@Test
	public void test() throws Exception {
		mockMvc.perform(get("/test2"))
						.andDo(print())
						.andExpect(status().is2xxSuccessful())
						.andReturn();
	}
	
}

수행하면 테스트가 성공함을 확인할 수 있다.

Standalone 테스트와 비교

Standalone 테스트에서의 변경사항을 확인해보면 다음과 같다.

스프링 환경과의 연동 설정을 수행한다.

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = {
	"file:src/main/webapp/WEB-INF/spring/root-context.xml",
	"file:src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml"
})

MockMvc에 환경정보를 설정하기 위해 WebApplicationContext 객체를 연결 생성한다.

환경이 연동되어 있을 때에만 자동 주입이 되므로 주의한다.

@Autowired
WebApplicationContext context;

MockMvc 객체를 생성할 때 .webAppContextSetup() 명령을 사용한다.

mockMvc = MockMvcBuilders.webAppContextSetup(context).build();

위와 같이 처리할 경우 Spring 요청 처리의 대부분을 확인할 수 있으며, 처리 결과는 다음과 같다.

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /test2
       Parameters = {}
          Headers = {}

Handler:
             Type = com.hakademy.spring11.controller.TestController
           Method = public java.lang.String com.hakademy.spring11.controller.TestController.test2()

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = test2
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = {}
     Content type = null
             Body = 
    Forwarded URL = /WEB-INF/views/test2.jsp
   Redirected URL = null
          Cookies = []

Last updated