<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
스프링부트에서는 @SpringBootTest 어노테이션을 통해 애플리케이션 테스트에 필요한 거의 모든 의존성들을 제공해준다.
@SpringBootTest 어노테이션은 Spring Main Application(@SpringBootApplication)을 찾아가 하위의 모든 Bean을 Scan한다.
그 후 Test용 Application Context를 만들면서 빈을 등록해주고, mock bean을 찾아가 그 빈만 mock bean으로 교체해준다.
@RestController
public class DsunniController {
@Autowired
private DsunniService dsunniService;
@GetMapping("/hello")
public String hello() {
return "hello " + dsunniService.getName();
}
}
package me.dsunni.sample;
import org.springframework.stereotype.Service;
@Service
public class DsunniService {
public String getName() {
return "dsunni";
}
}
webEnvironment
- MOCK : mock servlet environment으로 내장 톰캣 구동을 안한다
- RANDOM_PORT, DEFINED_PORT : 내장 톰캣 사용
- NONE : 서블릿 환경 제공 안함
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@AutoConfigureMockMvc
public class DsunniControllerTest {
@Autowired
MockMvc mockMvc;
@Test
public void hello() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/hello"))
.andExpect(status().isOk())
.andExpect(content().string("hello dsunni"))
.andDo(print());
}
}
@RunWith(SpringRunner.class) 어노테이션은 JUnit이 내장된 Runner를 실행할 때 SpringRunner.class라는 확장된 클래스를 실행하라고 지시한다.
테스트의 WebEnvironment 환경은 기본적으로 MOCK으로 잡혀져있다. 이 때 내장톰캣을 구동하지 않아 서블릿이 아니라 서블릿을 Mocking한 컨테이너가 뜬다.
Mockup이된 서블릿과 interaction 하기 위해서는 MockMVC라는 클라이언트를 사용해야한다.
참고로 강의를 들으면서 진행하던 중 MockMVC의 perform에서 get, post를 사용할 수 없는 에러가 떴다. 아마 너무 많은 static methods 중에서 어떤 것을 임포트시켜야할지 알 수 없어서 뜬거같다.
앞에 MockMvcRequestBuilders
를 추가
import org.junit.Test;
import org.junit.runner.RunWith;
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.web.client.TestRestTemplate;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
public class DsunniControllerTest {
@Autowired
TestRestTemplate testRestTemplate;
@Test
public void hello() throws Exception {
String result = testRestTemplate.getForObject("/hello", String.class);
assertThat(result).isEqualTo("hello dsunni");
}
}
WebEnvironment이 RANDOM_PORT를 사용할 때 실제로 서블릿 컨테이너(내장 톰캣)이 랜덤한 포트로 뜬다. 이때부터는 Test용 RestTemplate, Web Client를 사용한다.
String result = testRestTemplate.getForObject("/hello", String.class);
위의 테스트는 서비스, 컨트롤러단까지 모두 가서 효울적이지 않다. @MockBean을 붙여 서비스만 테스트해보자.
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
public class DsunniControllerTest {
@Autowired
TestRestTemplate testRestTemplate;
@MockBean
DsunniService mockDsunniService;
@Test
public void hello() throws Exception {
when(mockDsunniService.getName()).thenReturn("dsunni");
String result = testRestTemplate.getForObject("/hello", String.class);
assertThat(result).isEqualTo("hello dsunni");
}
}
@MockBean을 붙여서 mockDsunniService를 만들면 Application Context안의 DsunniService 빈을 mockDsunniService 빈으로 교체한다. 실질적으로 원본이 아닌 mock bean을 사용해 테스트할 수 있다.
Java5 Spring MVC WebFlux에 새로 추가된 Rest Client 중 하나이다. 기존에 사용하던 Rest Client는 Synchronous였다. 요청 하나 보내면 끝날때 까지 기다렸다가 요청을 보낼 수 있었다.
WebTestClient는 Asynchronous하게 동작한다. 요청을 보내고 응답이 오면 그 때 CallBack Event가 발생해 실행할 수 있다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class DsunniControllerTest {
@Autowired
WebTestClient webTestClient;
@MockBean
DsunniService mockDsunniService;
@Test
public void hello() throws Exception {
when(mockDsunniService.getName()).thenReturn("dsunni");
webTestClient.get().uri("/hello") //request 만들어서
.exchange() //보내고
.expectStatus().isOk() //검증
.expectBody(String.class).isEqualTo("hello dsunni");
}
}
레이어 별로 잘라서 테스트 하고 싶을 때 사용한다. 레이어 별로 빈이 등록됨
- @JsonTest
- @WebMvcTest
- @WebFluxTest
- @DataJpaTest
OutputCapture
TestPropertyValues
TestRestTemplate
ConfigFileApplicationContextInitializer