[Spring Boot] 스프링부트 활용 - 테스트

dsunni·2020년 8월 6일
0

🌵 스프링부트 핵심 기능

테스트

1. 의존성 추가

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

2. @SpringBootTest

스프링부트에서는 @SpringBootTest 어노테이션을 통해 애플리케이션 테스트에 필요한 거의 모든 의존성들을 제공해준다.

@SpringBootTest 어노테이션은 Spring Main Application(@SpringBootApplication)을 찾아가 하위의 모든 Bean을 Scan한다.

그 후 Test용 Application Context를 만들면서 빈을 등록해주고, mock bean을 찾아가 그 빈만 mock bean으로 교체해준다.

image

DsunniController.java

@RestController
public class DsunniController {
    @Autowired
    private DsunniService dsunniService;

    @GetMapping("/hello")
    public String hello() {
        return "hello " + dsunniService.getName();
    }
}

DsunniService.java

package me.dsunni.sample;

import org.springframework.stereotype.Service;

@Service
public class DsunniService {
    public String getName() {
        return "dsunni";
    }
}


3. WebEnvironment

webEnvironment

  • MOCK : mock servlet environment으로 내장 톰캣 구동을 안한다
  • RANDOM_PORT, DEFINED_PORT : 내장 톰캣 사용
  • NONE : 서블릿 환경 제공 안함

(1) MOCK

DsunniControllerTest

@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라는 클라이언트를 사용해야한다.


🚨 Cannot find Method 어쩌구 에러

참고로 강의를 들으면서 진행하던 중 MockMVC의 perform에서 get, post를 사용할 수 없는 에러가 떴다. 아마 너무 많은 static methods 중에서 어떤 것을 임포트시켜야할지 알 수 없어서 뜬거같다.

앞에 MockMvcRequestBuilders 를 추가


(2) RANDOM_PORT

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);

  • getForObject(url, 원하는 body타입)


@MockBean

위의 테스트는 서비스, 컨트롤러단까지 모두 가서 효울적이지 않다. @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을 사용해 테스트할 수 있다.



@WebTestClient

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>

DsunniControllerTest

@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");
    }   
}


4. 슬라이스 테스트

레이어 별로 잘라서 테스트 하고 싶을 때 사용한다. 레이어 별로 빈이 등록됨

  • @JsonTest
  • @WebMvcTest
  • @WebFluxTest
  • @DataJpaTest


5. 테스트유틸

  • OutputCapture

  • TestPropertyValues

  • TestRestTemplate

  • ConfigFileApplicationContextInitializer

profile
https://dsunni.tistory.com/ 이사갑니답

0개의 댓글