본문 바로가기
학습/Spring

[spring] 스프링부트의 테스트

by KKambi 2020. 4. 17.

스프링부트 테스트

준비물

  1. spring-boot-starter-test 의존성

    • junit (unit test framework)
    • jsonpath (json assertion)
    • mockito (mocking framework)
    • selenium (web scrapping)
  2. junit test runner를 설정하는 @RunWith(SpringRunner.class)

  3. 스프링부트 테스트 어노테이션 @SpringBootTest / @JsonTest / @WebMvcTest

    • @SpringBootTest의 기본 웹 환경은 WebEnvironment = SpringBootTest.WebEnvironment.MOCK
    • 테스트할 때, 원래의 서블릿 컨테이너 대신 mocking container를 띄운다.
    • 원래 서블릿처럼 DispatcherServlet과의 인터렉션이 가능
    • 이를 위해 mockmvc라는 클라이언트를 필수적으로 사용
1
2
3
4
5
@RunWith(SpringRunner.class)
@SpringBootTest(WebEnvironment = Webenvironment.MOCK)
public class SampleControllerTest {
 
}
cs

 


 

테스트 웹환경 MOCK

  • @SpringBootTest(WebEnvironment = SpringBootTest.WebEnvironment.MOCK)

 

MockMvc

  1. test controller에 @AutoConfigureMockMvc 추가
  2. @Autowired로 MockMvc 주입받기
  3. MockMvcRequestBuilders와 MockMvcRsultMatchers 패키지의 메소드를 이용하여 테스트
    • get() : org.springframework.test.web.servlet.request.MockMvcRequestBuilders 패키지
    • status() / content() / print() : org.springframework.test.web.servlet.result.MockMvcResultMatchers 패키지
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@AutoConfigureMockMvc
@RunWith(SpringRunner.class)
@SpringBootTest(WebEnvironment = Webenvironment.MOCK)
public class SampleControllerTest {
    
    @Autowired
    Mockmvc mockMvc;
 
    @Test
    public String hello(){
        mockMvc.perform(get("/hello"))
            .andExpect(status().isOk())
            .andExpect(content().string("hello world"))
            .andDo(print());
    }
}
cs

 


 

테스트 웹환경 RANDOM_PORT

  • @SpringBootTest(WebEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
  • MOCK과 달리, 실제 서블릿 컨테이너(ex. 내장 톰캣)가 랜덤 포트 위에서 동작

 

TestRestTemplate 이용

  • RestTemplate?
    • REST 서비스의 EndPoint를 호출할 수 있는 클라이언트 객체
    • Spring 3부터 지원
    • REST API 호출 이후 응답을 받을 때까지 기다리는 synchronous client
    • getForObject(URL, 반환타입) : HTTP GET 요청의 결과를 객체로 반환
1
2
3
4
5
6
7
8
9
10
11
12
13
@RunWith(SpringRunner.class)
@SpringBootTest(WebEnvironment = Webenvironment.RANDOM_PORT)
public class SampleControllerTest {
    
    @Autowired
    TestRestTemplaet testRestTemplate;
 
    @Test
    public String hello(){
        String result = testRestTemplate.getForObject("/hello"String.class);
        assertThat(result).isEqualTo("hello world");
    }
}
cs

 

WebTestClient 이용

  • spring-boot-starter-webflux 의존성 필요
  • WebTestClient?
    • Spring 5부터 지원
    • NonBlock + Async Web Client
    • 비동기를 지원하므로 실제 웹 클라이언트와 동일하게 API 사용가능
    • 편리함 : WebTestClient > MockMvc > TestRestTemplate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RunWith(SpringRunner.class)
@SpringBootTest(WebEnvironment = Webenvironment.RANDOM_PORT)
public class SampleControllerTest {   
 
    @Autowired
    WebTestClient webTestClient;
 
    @Test
    public String hello(){
        webTestClient.get().uri("/hello").exchange()
                    .expectStatus().isOk()
                    .expectBody(String.class).isEqualTo("hello world");
    }
}
cs

 


 

테스트 규모 제한하기

  • Controller가 Service를 주입받아, URL Get Mapping에서 서비스의 메소드를 실행한 결과값을 반환하는 상황
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Service
public class SampleService {
    public String getName() {
        return "world";
    }
}
 
@RestController
public class SampleController {
    private SampleService sampleService;
 
    SampleController(SampleService sampleService){
        this.sampleService = sampleService;
    }
    @GetMapping("/hello")
    public String hello(){
        return "hello " + sampleService.getName();
    }
}
cs
  • 컨트롤러만 테스트하고 싶은데, 주입받는 서비스 객체가 너무 크다면?
  • @MockBean
    • Application Context 안의 SampleService Bean을 MockBean으로 교체
    • 테스트 환경에서 해당 컨트롤러는 MockService를 사용
    • when()thenReturn()으로 MockBean의 메소드 반환값을 변경할 수 있음
    • @Test마다 리셋되므로, 테스트 메소드 내에서 반환값 변경 등에 대한 리셋을 신경쓸 필요 없음
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@RunWith(SpringRunner.class)
@SpringBootTest(WebEnvironment = Webenvironment.RANDOM_PORT)
public class SampleControllerTest {   
 
    @Autowired
    WebTestClient webTestClient;
 
    @MockBean
    SampleService sampleService;
 
    @Test
    public String hello(){
        when(mockSampleService.getName()).thenReturn("kkambi");      
 
        webTestClient.get().uri("/hello").exchange()
                    .expectStatus().isOk()
                    .expectBody(String.class).isEqualTo("hello kkambi");    
    }
}
cs

 


 

거대한 @SpringBootTest

  • 큰 규모의 통합 테스트
  • @SpringBootApplication을 찾아, 하위 패키지의 모든 빈을 스캔하고, 테스트용 Application Context에 등록
  • @MockBean이 있다면 그들만 교체

 

Slice Test

@SpringBootTest를 대체

  1. @JsonTest

    • 우리의 Model이 Json형태로 나갈 때
    • JacksonTester<ExampleObject>를 주입받음
    • 미리 만들어놓은 json와 비교하거나, json의 내용을 비교
  2. @WebMvcTest(SampleController.class)

    • 컨트롤러 하나만 테스트하고 싶을 때
    • Web과 관련된 Bean만 등록
    • @Controller, @ControllerAdvice, @JsonComponent, @Converter
    • @Service와 같은 일반적인 @Component는 등록되지 않음
    • Web Layer 아래 의존성이 모두 제거됨 -> 필요 의존성은 @MockBean으로 채워야 함 SampleService를 채운 것처럼!!
    • MockMvc 사용해야 함

 


 

테스트 유틸리티

스프링부트가 제공하는 테스트 유틸리티

  1. OutputCapture
    • 스프링부트 2.2.0부터 deprecated되었으므로 OutputCaptureRule을 이용
    • junit의 @Rule을 확장하여 만듬
    • 필수 : public으로 인스턴스를 생성해야 함
    • 특징 : 로그를 비롯해, 콘솔에 찍히는 모든걸 캡쳐
    • @Rule : junit에서 유연한 테스팅을 위해 제공하는 추가기능 같은 것들
  2. TestRestTemplate
  3. TestPropertyValues
  4. ConfigFileApplicationContextInitializer

댓글