단위 테스트를 수행할 때는 테스트 대상이 되는 객체가 최대한 단순해야 효율적인 테스트가 가능하다. 그러나 일반적으로 웹 애플리케이션은 테스트 개상 객체가 특정 서버와 연관되어 있거나 다른 객체들과 연관되어 관계가 복잡한 경우가 많다.
이러한 경우 해당 객체를 단독으로 테스트 할 수 있는 환경이 필요하다.
스프링부트 프로젝트를 생성하면 테스트 스타터는 자동으로 추가되고, 테스트 스타터가 등록되어 있기에 테스트에 필요한 여러 라이브러리들도 자동으로 추가가 된다.
스프링부트 프로젝트 생성 시 기본적으로 제공되는 테스트 케이스 소스는 아래와 같다.
package com.example.springboottest;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SpringbootTestApplicationTests {
@Test
void contextLoads() {
}
}
@SpringBootTest
SpringBootApplication
어노테이션이 사용자 작성 빈과 자동설정 빈들을 모두 초기화하는 것과 마찬가지로, 이 어노테이션 역시 테스트를 위해 필요한 모든 설정과 빈들을 자동으로 초기화한다.
@SpringBootTest
의 속성application.properties
## Test Property Setting
author.name=TESTER
author.age=24
SpringBootTestApplicationTests.java
package com.example.springboottest;
import com.example.springboottest.controller.BoardController;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.env.Environment;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(
classes = BoardController.class,
properties = { "author.name=Changed TESTER", "author.age=25" })
class SpringbootTestApplicationTests {
@Autowired
private Environment env;
@Test
void contextLoads() {
System.out.println("이름 : " + env.getProperty("author.name"));
System.out.println("나이 : " + env.getProperty("author.age"));
}
}
실행 결과는 아래와 같다
class 속성을 설정해주면 불필요한 객체는 메모리에 올라가지 않기 때문에 메모리 낭비를 줄일 수 있다
또 properties속성을 이용하면 application.properties 파일에 설정된 외부 프로퍼티를 재정의할 수 있고, 또는 새로운 프로퍼티를 추가할 수도 있다. (그냥 새로 쓰면 댐)
Mock : 테스트를 위해 만든 모형
Mocking : 테스트를 위해 실제 객체와 비슷한 모의 객체를 만드는 것
Mock-up : 모킹한 객체를 메모리에서 얻어내는 과정
🤷🏻♀️ 왜 굳이 모의 객체를 만드능가?
→ 객체 테스트를 위해서는 테스트 대상 객체가 메모리에 있어야하는데, 생성하는데 복잡한 객체는 테스트가 어렵다. ( 다른 소프트웨어의 도움이 필요한 경우가 있기도 하다 )
따라서 그냥 비슷한 가짜를 만들어서 테스트에 필요한 기능만 가지도록 모킹하면 테스트가 쉬워짐! 모킹한 객체를 이용하면 의존성을 단절시킬 수 있어서 쉽게 테스트 할 수 있다.
웹 애플리케이션 컨트롤러 테스트 시 서블릿 컨테이너를 모킹하기 위해 어노테이션을 이용하면 테스트가 간단해진다.
서블릿 컨테이너를 모킹한다?
→ 웹 환경에서 컨트롤러를 테스트하려면 서블릿 컨테이너가 구동되고, DispatcherServlet 객체가 메모리에 올라가야하는 복잡한 절차가 있다. 이를 모킹을 간단히 만들어주는 것
@WebMvcTest
어노테이션@WebMvcTest
는 @Controller
나 @RestController
가 설정된 클래스들을 찾아 메모리에 생성한다.
@SpringBootTest
와 함께 사용 불가능! (서로의 MockMvc를 모킹해서 충돌 발생)
package com.example.springboottest;
// import ...
@RunWith(SpringRunner.class)
@WebMvcTest
public class BoardControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testHello() throws Exception{
mockMvc.perform(get("/hello").param("name", "test1")).andExpect(status().isOk()).andExpect(content().string("Hello : test1" )).andDo(print());
}
}
테스트 결과는 아래와 같다
MockHttpServletRequest:
HTTP Method = GET
Request URI = /hello
Parameters = {name=[test1]}
Headers = []
Body = null
Session Attrs = {}
Handler:
Type = com.example.springboottest.controller.BoardController
Method = com.example.springboottest.controller.BoardController#hello(String)
...
...
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [Content-Type:"text/plain;charset=UTF-8", Content-Length:"13"]
Content type = text/plain;charset=UTF-8
Body = Hello : test1
Forwarded URL = null
Redirected URL = null
Cookies = []
Process finished with exit code 0
톰캣 서버가 구동되지 않고 테스트를 수행할 수 있다.
@AutoConfigureMockMvc
@SpringBootTest
에는 웹 애플리케이션 테스트를 지원하는 webEnvironment 속성이 있다. 이 속성을 생략하면 기본값으로 MOCK이 설정되어 서블릿 컨테이너가 모킹됨
→ 즉, 테스트 케이스 실행 시 서블릿 컨테이너를 구동하지 않는 것
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
설정으로 모킹한 객체를 의존성 주입 받기 위해서 @AutoConfigureMockMvc
를 추가해줘야 한다.
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@AutoConfigureMockMvc
public class BoardControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testHello() throws Exception{
mockMvc.perform(get("/hello").param("name", "test1")).andExpect(status().isOk()).andExpect(content().string("Hello : test1" )).andDo(print());
}
}
@WebMvcTest
는 @Controller
나 @RestController
가 설정된 클래스들을 찾아 메모리에 생성하는 반면에, @AutoConfigureMockMvc
는 @Controller
나 @RestController
외에도 @Repository
, @Service
도 찾아서 메모리에 올린다는 점이 차이점이다.
간단하게 컨트롤러만 테스트하는 경우에는
@WebMvcTest
사용하는 것이 좋다
MockMvc가 제공하는 perform()
: 브라우저에서 서버로 요청을 보내듯 컨트롤러를 실행시킨다
인자 : RequestBuilder 객체
RequestBuilder 객체는 MockMvcRequestBuilders의 정적 메소드를 이용하여 생성한다.
get()
) 반환값 : ResultActions
ResultActions
: 응답결과를 검증하는 andExpect()
메소드를 제공한다.