티스토리 뷰

스프링부트에서는 @SpringBootTest 를 통해서 Application Test에 필요한 거의 모든 기능을 제공한다. 

우선 사용하기 위해서는 의존성이 필요하다.

 

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

그러면 다음과 같이 가장 기본적인 형태로 Test코드를 작성할 수 있다. 

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
class SampleControllerTest {

}

@SpringBootTest 의 webEnviroment는 기본값으로 MOCK가 잡혀있는데, 이 경우는 서블릿 컨테이너를 띄우지 않고 MockUp을 하게 된다. 즉, Mocking된 서블릿이 뜸에 따라서 디스패처 서블릿에 요청을 보내는 것과 비슷하게 테스트를 할 수 있게 된다.

 

* MockUp ?
 사전적 의미로는 모형이라는 뜻으로 실 제품이 나오기 전 모형을 만드는 것을 뜻한다. 즉.. 개발에서 쓰이는 MockUp은 결국 프로그램

 혹은 앱을 가상의 모형과 같이 임시로 띄워놓고 거기에 작성한 프로그램이 정상적으로 작동하는지 테스트를 하는 것.

 

또한, @RunWithJUnit 프레임워크가 테스트를 실행할 시 테스트 실행방법을 확장할 때 쓰는 어노테이션이다.
쉽게 말해 JUnit 프레임워크가 내장된 Runner를 실행할 때 @Runwith 를 통해 SpringRunner.class 라는 확장된 클래스를 실행하라고 지시한 것으로 스프링 테스트 컨택스트 프레임워크의 모든 기능을 사용할 수 있게 된다.

 


1. MockMvc 테스트

그럼 실제로 MockMvc 테스트 코드를 짜 보자. 우선 MockUp된 서블릿에 인터랙션을 하기 위해서는 MockMvc라는 클라이언트를 사용해야 한다. 사용하기 위해서는 아래와 같이 @AutoConfigureMockMvc 를 추가하고 MockMvc를 주입받으면 된다.

 

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
class SampleControllerTest {

    @Autowired
    MockMvc mockMvc;
}

 

자 그럼 예시 코드를 살펴보자.

 

- Controller

@RestController
public class MyController {

    @Autowired
    MyService myService;

    @GetMapping("/test")
    public String hello() {

        return "hello " + myService.getName();
    }
}

- Service

@Service
public class MyService {

    public String getName() {
        return "chany";
    }
}

- Test

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@AutoConfigureMockMvc
public class MyControllerTest {

    @Autowired
    MockMvc mockMvc;

    @Test
    public void hello() throws Exception{
        mockMvc.perform(get("/test"))
                .andExpect(status().isOk())
                .andExpect(content().string("hello chany"))
                .andDo(print());

    }
}

mockMvcperform으로 get / post 등의 요청을 넣어주고 그때의 기댓값들을 하나씩 넣어주면 된다

 

여기서 한 가지 번외로, 연관관계에 있는 클래스를 @MockBean 으로 등록하면 Application 컨테이너 안에 들어있는 BeanMockBean으로 교체한다. 예시를 살펴보면 이해하기 쉽다. 

 

바로 위 테스트 코드와 같이 작성된 코드는 Service의 getName메서드가 "chany"라는 문자열을 return 하도록 되어있지만, 이 Service를 MockBean으로 등록하고 호출되었을 때 return 값을 yeol2로 변경하도록 하면 yeol2가 return된다.

 

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@AutoConfigureMockMvc
public class MyControllerTest {

    @Autowired
    MockMvc mockMvc;
    
    @Autowired
    MyService mockMyService;

    @Test
    public void hello() throws Exception{
    	
        when(mockMyService.getName()).thenReturn("yeol2");
    	
        mockMvc.perform(get("/test"))
                .andExpect(status().isOk())
                .andExpect(content().string("hello yeol2"))
                .andDo(print());

    }
}

 


2. 서블릿 컨테이너 테스트

이번에는 앞서 언급했던 WebEnviroment를 MOCK가 아닌 RANDOM_PORT를 사용하면 현재 사용 가능한 Random한 포트에 서블릿 컨테이너가 생성된다. (DEFINED_PORT를 통해서 포트를 지정할 수도 있다.)
이때는 MockMvc가 아니라 TestRestTemplate 혹은 TestWebClient를 사용해야 한다.

 

- TestRestTemplate

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
public class MyControllerTest {

    @Autowired
    TestRestTemplate testRestTemplate;

    @MockBean
    MyService mockMyService;

    @Test
    public void hello() throws Exception{

        when(mockMyService.getName()).thenReturn("yeol2");

        String result = testRestTemplate.getForObject("/test", String.class);
        assertThat(result).isEqualTo("hello yeol2");
    }
}

 

 

- WebClientTest

 

TestRestTemplate는 syncronous하다. 하나의 요청에 대한 응답이 끝날 때까지 기다렸다가 다음 요청을 보내고..
그러나 WebTestClient는 unsyncronous하다. 요청을 보내고 기다리는 게 아니라 응답이 오면 바로 Callback이 온다.
그래서 정말 WebClient와 유사한 api를 사용할 수 있다. 우선 WebTestClient를 사용하기 위해선 webflux 의존성이 필요하다.

 

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
public class MyControllerTest {

    @Autowired
    WebTestClient webTestClient;

    @MockBean
    MyService mockMyService;

    @Test
    public void hello() throws Exception{

        when(mockMyService.getName()).thenReturn("yeol2");

        webTestClient.get().uri("/test").exchange()
                .expectStatus().isOk()
                .expectBody(String.class).isEqualTo("hello yeol2");
    }
}

3. 슬라이스 테스트

지금까지 살펴본 @SpringBootTest 는 통합테스트다. 즉, SpringMainApplication을 찾아가서 여기서 시작하는 모든 Bean 스캔을 하고, 테스트용 Application Context에 등록하면서 MockBean을 찾아서 교체하는 과정을 거친 뒤 테스트가 실행된다.

 

만약, 딱 테스트하고 싶은 것들만 Bean으로 등록하여 테스트를 하고자 할 때는 슬라이스 테스트를 해야 한다.

 

@RunWith(SpringRunner.class)
@WebMvcTest(MyController.class)
public class MyControllerTest {

    @Autowired
    MockMvc mockMvc;

    @MockBean
    MyService mockMyService;

    @Test
    public void hello() throws Exception{

        when(mockMyService.getName()).thenReturn("yeol2");

        mockMvc.perform(get("/test"))
                .andExpect(status().isOk())
                .andExpect(content().string("hello yeol2"));
    }
}

MockMvc의 경우 @WebMvcTest 에 테스트하고자 하는 Controller만 지정해주면 되는데, 이때는 앞서 언급한 대로 Bean등록이 이루어지지 않기 때문에 의존성이 다 끊겨버린다.
즉, Repository, Service와 같이 사용하는 의존성이 있다면 MockBean으로 모두 등록해주어야 한다.

 


@ 출처

 

 스프링 부트 개념과 활용 - inflearn, by 백기선님

 

 

 

 

 

댓글
링크
최근에 올라온 글
Total
Today
Yesterday