티스토리 뷰
스프링부트에서는 @SpringBootTest 를 통해서 Application Test에 필요한 거의 모든 기능을 제공한다.
우선 사용하기 위해서는 의존성이 필요하다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
그러면 다음과 같이 가장 기본적인 형태로 Test코드를 작성할 수 있다.
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
class SampleControllerTest {
}
@SpringBootTest 의 webEnviroment는 기본값으로 MOCK가 잡혀있는데, 이 경우는 서블릿 컨테이너를 띄우지 않고 MockUp을 하게 된다. 즉, Mocking된 서블릿이 뜸에 따라서 디스패처 서블릿에 요청을 보내는 것과 비슷하게 테스트를 할 수 있게 된다.
* MockUp ?
사전적 의미로는 모형이라는 뜻으로 실 제품이 나오기 전 모형을 만드는 것을 뜻한다. 즉.. 개발에서 쓰이는 MockUp은 결국 프로그램
혹은 앱을 가상의 모형과 같이 임시로 띄워놓고 거기에 작성한 프로그램이 정상적으로 작동하는지 테스트를 하는 것.
또한, @RunWith 는 JUnit 프레임워크가 테스트를 실행할 시 테스트 실행방법을 확장할 때 쓰는 어노테이션이다.
쉽게 말해 JUnit 프레임워크가 내장된 Runner를 실행할 때 @Runwith 를 통해 SpringRunner.class 라는 확장된 클래스를 실행하라고 지시한 것으로 스프링 테스트 컨택스트 프레임워크의 모든 기능을 사용할 수 있게 된다.
1. MockMvc 테스트
그럼 실제로 MockMvc 테스트 코드를 짜 보자. 우선 MockUp된 서블릿에 인터랙션을 하기 위해서는 MockMvc라는 클라이언트를 사용해야 한다. 사용하기 위해서는 아래와 같이 @AutoConfigureMockMvc 를 추가하고 MockMvc를 주입받으면 된다.
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
class SampleControllerTest {
@Autowired
MockMvc mockMvc;
}
자 그럼 예시 코드를 살펴보자.
- Controller
@RestController
public class MyController {
@Autowired
MyService myService;
@GetMapping("/test")
public String hello() {
return "hello " + myService.getName();
}
}
- Service
@Service
public class MyService {
public String getName() {
return "chany";
}
}
- Test
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@AutoConfigureMockMvc
public class MyControllerTest {
@Autowired
MockMvc mockMvc;
@Test
public void hello() throws Exception{
mockMvc.perform(get("/test"))
.andExpect(status().isOk())
.andExpect(content().string("hello chany"))
.andDo(print());
}
}
mockMvc에 perform으로 get / post 등의 요청을 넣어주고 그때의 기댓값들을 하나씩 넣어주면 된다.
여기서 한 가지 번외로, 연관관계에 있는 클래스를 @MockBean 으로 등록하면 Application 컨테이너 안에 들어있는 Bean을 MockBean으로 교체한다. 예시를 살펴보면 이해하기 쉽다.
바로 위 테스트 코드와 같이 작성된 코드는 Service의 getName메서드가 "chany"라는 문자열을 return 하도록 되어있지만, 이 Service를 MockBean으로 등록하고 호출되었을 때 return 값을 yeol2로 변경하도록 하면 yeol2가 return된다.
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@AutoConfigureMockMvc
public class MyControllerTest {
@Autowired
MockMvc mockMvc;
@Autowired
MyService mockMyService;
@Test
public void hello() throws Exception{
when(mockMyService.getName()).thenReturn("yeol2");
mockMvc.perform(get("/test"))
.andExpect(status().isOk())
.andExpect(content().string("hello yeol2"))
.andDo(print());
}
}
2. 서블릿 컨테이너 테스트
이번에는 앞서 언급했던 WebEnviroment를 MOCK가 아닌 RANDOM_PORT를 사용하면 현재 사용 가능한 Random한 포트에 서블릿 컨테이너가 생성된다. (DEFINED_PORT를 통해서 포트를 지정할 수도 있다.)
이때는 MockMvc가 아니라 TestRestTemplate 혹은 TestWebClient를 사용해야 한다.
- TestRestTemplate
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
public class MyControllerTest {
@Autowired
TestRestTemplate testRestTemplate;
@MockBean
MyService mockMyService;
@Test
public void hello() throws Exception{
when(mockMyService.getName()).thenReturn("yeol2");
String result = testRestTemplate.getForObject("/test", String.class);
assertThat(result).isEqualTo("hello yeol2");
}
}
- WebClientTest
TestRestTemplate는 syncronous하다. 하나의 요청에 대한 응답이 끝날 때까지 기다렸다가 다음 요청을 보내고..
그러나 WebTestClient는 unsyncronous하다. 요청을 보내고 기다리는 게 아니라 응답이 오면 바로 Callback이 온다.
그래서 정말 WebClient와 유사한 api를 사용할 수 있다. 우선 WebTestClient를 사용하기 위해선 webflux 의존성이 필요하다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
public class MyControllerTest {
@Autowired
WebTestClient webTestClient;
@MockBean
MyService mockMyService;
@Test
public void hello() throws Exception{
when(mockMyService.getName()).thenReturn("yeol2");
webTestClient.get().uri("/test").exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("hello yeol2");
}
}
3. 슬라이스 테스트
지금까지 살펴본 @SpringBootTest 는 통합테스트다. 즉, SpringMainApplication을 찾아가서 여기서 시작하는 모든 Bean 스캔을 하고, 테스트용 Application Context에 등록하면서 MockBean을 찾아서 교체하는 과정을 거친 뒤 테스트가 실행된다.
만약, 딱 테스트하고 싶은 것들만 Bean으로 등록하여 테스트를 하고자 할 때는 슬라이스 테스트를 해야 한다.
@RunWith(SpringRunner.class)
@WebMvcTest(MyController.class)
public class MyControllerTest {
@Autowired
MockMvc mockMvc;
@MockBean
MyService mockMyService;
@Test
public void hello() throws Exception{
when(mockMyService.getName()).thenReturn("yeol2");
mockMvc.perform(get("/test"))
.andExpect(status().isOk())
.andExpect(content().string("hello yeol2"));
}
}
MockMvc의 경우 @WebMvcTest 에 테스트하고자 하는 Controller만 지정해주면 되는데, 이때는 앞서 언급한 대로 Bean등록이 이루어지지 않기 때문에 의존성이 다 끊겨버린다.
즉, Repository, Service와 같이 사용하는 의존성이 있다면 MockBean으로 모두 등록해주어야 한다.
@ 출처
스프링 부트 개념과 활용 - inflearn, by 백기선님
'프로그래밍 > Spring Boot' 카테고리의 다른 글
[Spring Boot] 7. Log 설정 (0) | 2021.03.10 |
---|---|
[Spring Boot] 6. profile 설정 (0) | 2021.03.08 |
[Spring Boot] 5. 외부 설정 (0) | 2021.03.07 |
[Spring Boot] 4. Spring Boot 특징 - 기본편 (0) | 2021.03.02 |
[Spring Boot] 3. 자동 설정 구현(2) (0) | 2021.03.02 |