ABOUT ME

-

오늘
-
어제
-
-
  • SpringBoot - Test(테스트)
    Back-end/Spring 2020. 6. 27. 23:11

    Test(테스트)

    코드를 작성하고 해당 코드에 대해서 테스트가 필요할 때 사용하는 방법들을 공부해보겠습니다.
    TDD(테스트 주도형 개발)가 중요한 만큼 테스트의 중요성이 높으니 코딩시 꼭 테스트하는 습관을 길러보도록 합시다.

    테스트 환경 구성하기

    새로운 패키지에 Controller, Service 클래스를 생성합니다.

    SampleController.class

    @RestController
    public class SampleController {
    
        @Autowired
        private SampleService sampleService;
    
        @GetMapping("/hello")
        public String hello() {
            return "hello " + sampleService.getName();
        }
    }

    SampleService.class

    @Service
    public class SampleService {
        public String getName() {
            return "junjang";
        }
    }

     

    test

    Service를 주입받고 hello 메소드를 구현합니다.

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

     

    test

    테스트를 위해선 꼭 테스트 의존성이 추가되어 있어야 합니다.
    pom.xml에 추가합니다.

    그리고 테스트를 하기 위해 테스트클래스를 따로 만들어주어야 합니다.

    SampleControllerTest.class

    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class ApplicationTests {
    
        @Test
        public void contextLoads() {
        }
    
    }

    기본적인 테스트 클래스의 구조는 위 코드와 같습니다.

    테스트를 하는 4가지 방법

    1. MockMVC

    MockMVC ?

    • 내장 Tomcat을 생략한 테스트입니다.
    • 서블릿을 Mocking한 것이 구동됩니다.

    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())
                    .andExpect(content().string("hello junjang"))
                    .andDo(print());
    
        }

     

    test
    • @SpringBootTest에 환경을 MOCK으로 설정합니다.
    • @AutoConfigureMockMvc를 추가하여야만 MockMVC를 사용할 수 있습니다.
    • @Autowired로 MockMVC 의존성을 가져옵니다.
    • Get방식으로 hello 경로로 status 200을 출력하고 문자열을 확인하여 출력합니다.

    테스트를 실행하면 테스트로그가 결과값으로 출력됩니다.

    RANDOM_PORT

    RANDOM_PORT ?

    • 내장 Tomcat을 포함한 테스트입니다.
    • 사용 가능한 포트를 자동으로 구동시켜줍니다.
    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

    2. TestRestTemplate

    또 다른 테스트 방법입니다.

    import static org.assertj.core.api.Assertions.assertThat;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    public class SampleControllerTest {
    
        @Autowired
        TestRestTemplate testRestTemplate;
    
        @Test
        public void hello() throws Exception {
            String result = testRestTemplate.getForObject("/hello", String.class);
            assertThat(result).isEqualTo("hello junjang");
        }
    
    }

     

    test
    • @Autowired로 TestRestTemplate를 주입받습니다.
    • 경로와 문자열을 result 변수에 선언하고 assertThat으로 비교합니다.

    @MockBean

    @SpringBootTest@SpringBootApplication이 구동할 때 등록하는 모든 빈을 전부 등록한 후 테스트를 하게 됩니다.
    그렇게 되면 테스트가 너무 무거워지고 효율성이 떨어지기 됩니다.
    그래서 @MockBean을 사용하여 필요한 빈을 Mock의 객체로 교체하여 해당 객체만 테스트시 적용되므로 가볍고 간편하게 테스트가 가능합니다.

    import static org.assertj.core.api.Assertions.assertThat;
    import static org.mockito.Mockito.when;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    public class SampleControllerTest {
    
        @Autowired
        TestRestTemplate testRestTemplate;
    
        @MockBean
        SampleService mockSampleService;
    
        @Test
        public void hello() throws Exception {
            when(mockSampleService.getName()).thenReturn("kimjunhyeung");
            String result = testRestTemplate.getForObject("/hello", String.class);
            assertThat(result).isEqualTo("hello kimjunhyeung");
        }
    
    }
    • Controller에서 사용하는 Service를 MockBean으로 주입합니다.
    • when 함수를 사용하여 MockBean에 등록된 메소드를 return합니다.
    • MockBean의 객체는 Mock에 등록된 빈이기 때문에 result도 받는 값이 MockBean의 return값과 동일해야 합니다.

    3. WebTestClient

    • Spring5의 WebFlux에 추가된 RestClient 중 하나로 Asynchronous(비동기식) 방식입니다.
      (기존의 RestClient는 Synchronous(동기식))
    • 사용하려면 webflux 의존성을 추가해야합니다.
    • REST방식을 테스트하기에 매우 용이합니다.
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>

     

    test

    webflux 의존성을 추가합니다.

    test
    • @Autowired를 통해 WebTestClient를 주입받습니다.
    • 주입받은 WebTestClient로 테스트 합니다.

    4. 슬라이스 테스트

    레이어 별로 잘라서 원하는 부분만 테스트하고 싶을 때 사용하며 가볍게 테스트 할 수 있습니다.

    • JsonTest
      참고자료 : 스프링공식문서 - JsonTest
    • WebMVCTest
      참고자료 : 스프링공식문서 - WebMvcTest
    • @Controller, @ControllerAdvice, @JsonComponent, Converter, GenericConverter, Filter, WebMvcConfigurer, HandlerMethodArgumentResolver 만 빈 으로 등록됩니다.
    • 사용할 빈이 있다면 @MockBean으로 만들어서 다 채워줘야 함 그래야 Controller이 필요한 빈을 주입받을 수 있습니다.
    • @WebMvcTest는 MockMvc로 테스트 해야합니다.
    • WebFluxTest
    • DataJPATest
    • etc..

    OutputCapture

    • 로그를 비롯해서 Console에 찍히는 모든 것을 다 캡쳐합니다.
    • 로그 메세지가 어떻게 찍히는지 테스트 해볼 수 있습니다.

    Logger logger = LoggerFactory.getLogger(SampleController.class);
    
        @Autowired
        private SampleService sampleService;
    
        @GetMapping("/hello")
        public String hello() {
            logger.info("baeksu");
            System.out.println("job");
            return "hello " + sampleService.getName();
        }

    hello 메소드에 logger, System.out.println를 선언합니다.

    @Rule
        public OutputCapture outputCapture = new OutputCapture();
    
        @Autowired
        WebTestClient webTestClient;
    
        @MockBean
        SampleService mockSampleService;
    
        @Test
        public void hello() throws Exception {
            when(mockSampleService.getName()).thenReturn("kimjunhyeung");
    
            webTestClient.get().uri("/hello").exchange().expectStatus().isOk()
                    .expectBody(String.class).isEqualTo("hello kimjunhyeung");
    
            assertThat(outputCapture.toString())
                    .contains("baeksu")
                    .contains("job");
        }
    • @Rule을 사용하여 OutputCapture 객체를 생성합니다.(public 항상 사용)
    • outputCapture 함수를 사용하여 hello 메소드에 포함된 단어를 선언합니다.
    test

    이상 없이 두 문자열이 테스트에 통과되고 출력된 것을 볼 수 있습니다.

    'Back-end > Spring' 카테고리의 다른 글

    SpringBoot - MVC Setting  (0) 2020.06.27
    SpringBoot - Devtools  (0) 2020.06.27
    SpringBoot - Logging(로깅)  (0) 2020.06.27
    SpringBoot - Profile(프로파일)  (0) 2020.06.27
    SpringBoot - Configuration(외부설정)  (0) 2020.06.27

    댓글