Spring/boot

[Spring-boot] Test

jay Joon 2021. 1. 18. 00:24

Test

우리는 Spring-boot를 사용하면 대부분 WebApplication을 개발할 것이다.

WebApplication을 개발 함에 있어 또한 대부분 MVC 패턴을 이용하여 개발을 진행한다.

또한 개발을 진행함에 있어 Test 코드의 작성은 선택이 아닌 필수이다.

 

Spring-boot는 Test를 작성함에있어 그렇게 어렵지 않으니 한번 알아보자.

 

자 다음과 같은 SampleContoller와 SampleService 가 있다.

@RestController
public class SampleController {

    @Autowired
    SampleService sampleService;

    @GetMapping("hello")
    public String helloName(){
        return sampleService.getName()+"hello";
    }
}

 

 

@Service
public class SampleService {

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

SampleController는 "/hello"이라는 요청이 들어오면 SampleService의 getName()에서 jaeJoon을 받은 후

 

RestController 임으로 응답의 Body 부분에 jaeJoonhello 를 리턴해 줄 것이다.

 

 

이러한 흐름을 Test 코드를 통해 검증 하고 싶을 때 다음과 같이 진행하면 된다.

 

  1. @SpringBootTest

    -> 1-1) webApplication 통합 테스트를 진행한다.(default: 내장 톰캣을 사용하지 않고)

    image

    ​ 위와 같이 Mock up 된 웹 환경을 조작하기 위해선 MockMvc를 통한 조작을 해야 함

    -> 1-2) Junit4 사용 시 @RunWith(SpringRunner.class) 와 함께 사용해야함( Junit5 사용시 적용하지 않아도 됨)

  2. @AutoConfigureMockMvc

    -> 위에서 말한 MockMvc를 주입받기 위한 애노테이션

자 이제 활용해보자.

@SpringBootTest
@AutoConfigureMockMvc
class SampleControllerTest {

    @Autowired
    MockMvc mockMvc;

    @Test
    void hello() throws Exception{
        // 테스트코드 작성 !
    }
}

 

이제 MockMvc의 Method를 알아보자.

 

크게 다음과 같이 3가지로 나눌 수 있다.

  1. perform()

    -> 어떠한 요청을 실행할 것인지 결정하는 Method (ex: get("url") , post("url") , put("url") 등등 )

  2. andDo()

    -> 요청을 실행하면서 할 행동

  3. andExpect()

    -> 요청의 기댓값 비교

대충 이렇게 설명할 수 있는데 3가지 Method를 체이닝 하여 이용한다.

 

 

이제 이용해 보면..

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

다음과 같이 작성할 수 있는데..

perform 안에 있는 get()이라는 Method는 org.springframework.test.web.servlet.request.MockMvcRequestBuil ders에서

온다.

 

말 그대로 get 요청으로 /hello를 호출한다고 보면 된다.

 

또한 print() , status(). content 들은

org.springframework.test.web.servlet.result.MockMvcResultMatche rs에서 온 녀석들임으로 결과의 Response 값들을 출력하거나 확인할 수 있는 Method 들이라고 생각하자.

 

지금은 ApplicationContext 안에 들어 있는 Bean들을 그대로 이용하여 Test 코드를 작성했다.

하지만 때때로 ApplicationContext 안에 있는 Bean들을 그대로 이용해서 Test 코드를 작성하기에 힘든 경우가 발생할 수 있다.

 

 

그러한 경우 ApplicationContext 안에 있는 Bean을 Mock Up 하여 Test 코드 환경에서 필요한 Bean을 주입할 수 있다. 다음과 같이 작성하면 된다.

@MockBean
SampleService sampleService;

 

일단 Mock up 할 클래스를 @MockBean 애노테이션을 붙여 선언 후

@Test
void hello() throws Exception{
    when(sampleService.getName()).thenReturn("mock Name"); // Test에서 사용할 getName() 의 리턴 값을 결정!
    mockMvc.perform(get("/hello"))
            .andExpect(status().isOk())
            .andDo(print())
            .andExpect(content().string("mock Namehello"));
}

다음과 같이 /hello라는 요청이 들어와서 SampleService 에있는 getName을 호출하게 되면 Test 코드에서 임시로 만든 Mock SampleService 주입하여 Test코드를 유연하게 짤 수 있다.

 

이러한 기능은 통합 테스트 환경이 아닌 단위 테스트 환경에서 더욱 큰힘을 발휘한다.

 

 

앞서 말한 통합테스트 환경이아닌 단위테스트 환경으로 변경하기 위해 선 @SpringBootTest 가 아닌 @WebMvcTest를 사용해보자.

@WebMvcTest(SampleController.class)
@AutoConfigureMockMvc
class MockSampleControllerTest {

    @Autowired
    MockMvc mockMvc;

    @Test
    void hello() throws Exception{
        mockMvc.perform(get("/hello"))
                .andExpect(status().isOk())
                .andDo(print())
                .andExpect(content().string("mock Namehello"));
    }
}

다음과 같이 @WebMvcTest(SampleController.class) 즉 SampleController 만 테스트를 진행하고 싶을 때

이 상태로 실행하게 된다면 다음과 같은 오류를 만나보게 될 것이다.

image

SampleService의 Bean을 찾지 못하여 생기는 오류인데 당연히 통합 테스트가 아닌 단위 테스트 임으로 SampleService 빈은 등록되지 않는다.

 

 

이러한 경우 앞서 살펴보았던 MockBean을 이용하여 가짜 Bean을 등록하여 테스트를 진행할 수 있다.

@WebMvcTest(SampleController.class)
@AutoConfigureMockMvc
class MockSampleControllerTest {


    @Autowired
    MockMvc mockMvc;

    @MockBean
       SampleService sampleService;

    @Test
    void hello() throws Exception{
        when(sampleService.getName()).thenReturn("mock Name");
        mockMvc.perform(get("/hello"))
                .andExpect(status().isOk())
                .andDo(print())
                .andExpect(content().string("mock Namehello"));
    }
}

 

앞서 말한 단위 테스트 말고도 spring boot는 더 많은 단위 테스트를 제공함으로 자세히 알고 싶다면 spring boot doc에서 test 부분을 살펴보자.

https://docs.spring.io/spring-boot/docs/2.4.0/reference/html/appendix-test-auto-configuration.html#test-auto-configuration