의존성을 확인하자
spring-boot-starter-test가 test 기능을 해준다.
scope은 test로 지정하면 된다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
test 만들기 단축키: Alt+Insert
@SpringBootTest
@SpringBootTest는 Spring main application, 즉 @SpringBootApplication을 찾아가서 이하의 모든 빈을 스캔하고 test용 application에 등록한다.
어마어마한 통합테스트인 것이다.
그리고 밑에 나오지만, MockBean만 교체해준다.
가장 기본적인 테스트 형태는 아래와 같다.
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest // (webEnvironment = SpringBootTest.WebEnvironment.MOCK)
class SampleControllerTest {
}
@SpringBootTest는 @RunWith(SpringRunner.class)와 같이 써야 한다.
@SpringBootTest의 default 웹 환경 설정값은 MOCK 이다.
웹 환경 설정: MOCK
웹 환경 설정의 기본값이다.
내장 톰캣을 구동하지 않는다.
Mocking한 DispatcherServlet을 사용해서 테스트 하겠다는 뜻이다.
하지만 mock up된 서블릿과 interaction을 하려면, MockMvc라는 클라이언트를 꼭 사용해야 한다.
@AutoConfigureMockMvc을 이용하는 것이 MockMvc를 이용하는 가장 간단한 방법이다.
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.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.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;
@RunWith(SpringRunner.class)
@SpringBootTest // (webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@AutoConfigureMockMvc
public class SampleControllerTest {
@Autowired
MockMvc mockMvc;
@Test
public void hello() throws Exception{
mockMvc.perform(get("/hello"))
.andExpect(status().isOk()) // 200이길 바라고
.andExpect(content().string("hello jueun")) // 내용이 이거 이고
.andDo(print()); // 요청 온 것을 찍어줬음 좋겠다.
}
}
웹 환경 설정: RANDON_PORT, DEFINED_PORT
RANDON_PORT: 내장 톰캣 사용하여 test용 서블릿이 랜덤한 포트에 뜬다.
DEFINED_PORT: 내장 톰캣 사용하여 test용 서블릿이 지정한 포트에 뜬다.
톰캣 서버에서 요청을 보내고 응답을 받아 확인하고 있다.
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class SampleControllerTest {
@Autowired // test용 RestTemplate
TestRestTemplate testRestTemplate;
@Test
public void hello() throws Exception{
// 이 body type에 있는 객체를 받음.
String result = testRestTemplate.getForObject("/hello", String.class);
assertThat(result).isEqualTo("hello jueun");
}
}
웹 환경 설정: NONE
NONE은 서블릿 환경 제공하지 않는다는 의미이다.
@MockBean
ApplicationContext에 들어있는 빈을 Mock으로 만든 객체로 교체 함.
모든 @Test 마다 자동으로 리셋된다.
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class SampleControllerTest3 {
@Autowired // test용 RestTemplate
TestRestTemplate testRestTemplate;
@MockBean
SampleService mockSampleService;
@Test
public void hello() throws Exception{
when(mockSampleService.getName()).thenReturn("hoit");
String result = testRestTemplate.getForObject("/hello", String.class);
assertThat(result).isEqualTo("hello hoit");
}
}
SampleController가 쓰는 SampleService를 모킹해서 빈을 교체했다.
ApplicationContext 안에 있는 SampleService 빈을 Mock으로 만든 mockSampleService로 교체한다.
그래서 이 테스트에서 Service는 원본 SampleService가 아닌 mockSampleService를 쓰게 된다.
WebTestClient
먼저, spring-boot-starter-webflux 의존성을 추가해야 사용할 수 있다.
@AutoConfigureWebTestClient 도 추가해야한다.
WebTestClient 빈을 만들어 주는 역할인데, 이를 안써도 webflux 의존성을 추가하면 정상작동하긴 한다.
기존에 사용하던 RestClient는 synchronous였는데 webClient는 asynchronous 이다.
synchronous: 요청하나 보내고 끝날 때까지 기다린 다음에 그 다음 요청을 보낼 수 있음
asynchronous: 요청을 보내고 기다리는 것이 아니라 응답이 오면 그 때 call back이 옴
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureWebTestClient
class SampleControllerTest4 {
@Autowired
WebTestClient webTestClient;
@MockBean
SampleService mockSampleService;
@Test
public void hello() throws Exception{
when(mockSampleService.getName()).thenReturn("hoit");
webTestClient.get().uri("/hello").exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("hello hoit");
}
}
하지만 꼭 이것 때문이 아니더라도 api가 편해서 쓴다고 한다.
(+ 추가로, mockMvc이 두번째로 불편하고, RestTemplate이 제일 불편하다고 한다.)
슬라이스 테스트
레이어 별로 잘라서 테스트할 수 있다.
통합 테스트인 @SpringBootTest 를 바꿔서 쓴다.
@JsonTest
레퍼런스: #
@WebMvcTest
레퍼런스: #
@Controller 들만 빈으로 등록된다.
WebMvcTest auto-configures the Spring MVC infrastructure and limits scanned beans to @Controller, @ControllerAdvice, @JsonComponent, 웹 관련들만.
Regular @Component and @ConfigurationProperties beans are not scanned.
@RunWith(SpringRunner.class)
@WebMvcTest(SampleController.class)
class SampleControllerTest6 {
@Autowired // WebMvcTest는 항상 MockMvc로 test 해야 한다.
MockMvc mockMvc;
@MockBean // Service는 등록이 되지 않기 때문에 사용하는 의존성을 채워줘야 한다.
SampleService mockSampleService;
@Test
public void hello() throws Exception{
when(mockSampleService.getName()).thenReturn("jueun");
mockMvc.perform(get("/hello"))
.andExpect(status().isOk())
.andExpect(content().string("hello jueun"));
}
}
@WebFluxTest
레퍼런스: #
@DataJpaTest
레퍼런스: #
@Repository 들만 빈으로 등록된다.
이외에도 많이 있다.
OutputCaptureRule
특정 로그 메시지가 출력이 되는지 테스트 코드로 확인하고 싶을 때 확인할 수 있다.
@Rule 어노테이션에 의해 public으로 선언해야한다.
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.system.OutputCaptureRule;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
@RunWith(SpringRunner.class)
@WebMvcTest(SampleController.class)
public class SampleControllerTestUtil {
@Rule
public OutputCaptureRule output = new OutputCaptureRule();
@MockBean // SampleController가 쓰는 SampleService를 모킹해서 빈을 교체했다.
SampleService mockSampleService;
@Autowired
MockMvc mockMvc;
@Test
public void hello() throws Exception{
when(mockSampleService.getName()).thenReturn("hoit");
mockMvc.perform(get("/hello2"))
.andExpect(content().string("hello hoit"));
assertThat(output.toString())
.contains("info level")
.contains("sout");
}
}
@RestController
public class SampleController {
@Autowired
private SampleService sampleService;
Logger logger = LoggerFactory.getLogger(SampleController.class);
@GetMapping("/hello2")
public String hello2() {
logger.info("info level");
System.out.println("sout");
return "hello " + sampleService.getName();
}
}
'Springboot' 카테고리의 다른 글
[spring boot] WebJars 중 jquery 추가해보기 (0) | 2021.01.10 |
---|---|
[spring boot] 정적 리소스 지원 (0) | 2021.01.10 |
[spring boot] HttpMessageConverters (0) | 2021.01.10 |
[spring boot] 로거를 Log4j2로 변경하기 (0) | 2021.01.08 |
[spring boot] logging 커스터마이징 (0) | 2021.01.08 |
[spring boot] logging 기본 설정 (0) | 2021.01.08 |