본문 바로가기

FastCampus - 한번에 끝내는 Java|Spring 웹 개발/03 스프링 입문

스프링 Ch 8-2~3 Spring 테스트 - 패스트캠퍼스 챌린지 14일차

오늘 볼 강의는 WebMVC 쪽의 테스트 작성이네요. 궁금했던 부분이니만큼 실습을 하면서 강의에서 자세히 설명해주지 않은 부분들을 이해해봐야겠습니다. 특히 이번건 스리슬쩍 넘어간 거 같은 부분도 있구요.

@Mock, @MockBean

먼저 이해해야할 건 이 두 가지 어노테이션이 무엇을 의미하느냐입니다.

short answer: mock 객체 생성을 대신 해줍니다.

일반적으로 모킹 프레임워크는 mock 객체를 만드는 함수를 제공해주죠? 필드를 초기화하는 것조차 귀찮았는지, 아니면 다른 이유가 있는건지 @Mock / @MockBean 을 필드에 넣어서 이를 대신합니다.

  • @Mock: org.mockito.Mock: 필드를 mock 객체로 만듭니다.
  • @MockBean: 스프링 건데, 위에 더해서 DI 컨테이너에 Bean 으로 만들어서 넣기까지 합니다.

직접 확인해보니 kotlin 에서는 lateinit 필드면 충분합니다. mock 필드의 초기화는 다음 섹션에서...

@WebMvcTest
class CalcHandlerTest {
    @MockBean
    private lateinit var calculator: CalcService
    ...

@SpringBootTest / @WebMvcTest

이런 어노테이션을 클래스에 장식해서 Spring의 테스트로서 동작하게 만들어야 합니다.

  • @SpringBootTest - 통합 테스트 (integration test) 용으로, 전체 spring context를 로드한다고 합니다. 지금으로서는 불필요해보이네요. (대충 훑어봤던 어느 글에서 ApplicationContext 에 대한 언급이 있었는데...)
    • 강의에서는 contextLoads() 라는 함수를 실행해서 스프링 컨텍스트가 로드되는 것만을 보여주셨습니다.
      메인 앱과 같은 패키지에 있어야 하는 이유는 추가 설정을 피하기 위해서인가보네요.
    • API 문서에서는 Spring TestContext Framework 에 추가로 Spring Boot 용 설정을 더한 것으로 묘사하고 있습니다.
    • /// Spring Initializr 가 생성한 템플릿 코드
      @SpringBootTest
      class Test456ApplicationTests {
          @Test
          fun contextLoads() {
          }
      }
  • @WebMvcTest - MVC 컨트롤러의 테스트에 관련된 것만 로드하는 모양입니다.
    • 강의에서는 일부 클래스를 어노테이션으로 추가로 수동으로 등록하고 있습니다.
    • API 문서에서 이 점을 더 자세히 설명하는데, Spring MVC 외의 관계없는 어노테이션들을 자동으로 로드하지 않는다고 하네요 (@Component, @Repository, @Service 등). 대신 WebMVC와 관련있는 것들은 로드하는 모양입니다 (@Controller, @Filter, ...).
    • MockMvc 클래스를 주입받아 사용합니다.
    • 코드는 다음 섹션에서
  • @Import(클래스 배열): 추가로 필요한 @Component 가 있으면 임포트합니다.
    • @WebMvc 의 경우 @Service 등이 import 되지 않으므로 이런 클래스가 필요하다면 추가해줘야 합니다.
    • @MockBean 이 달려있는 경우 @Import 는 불필요한 듯 합니다. (코드로 확인했으나 불명확함)
    • kotlin 에서는... Java의 배열을 나타내는 {} 도 필요없고 그냥 클래스 리스트를 적으면 됩니다.
      @Import(SayHelloService::class, CalcService::class)​
  • 관련 링크

@BeforeEach 에서 mock 초기화

@MockBean 으로 생성된 mock 객체의 mock 동작은 @BeforeEach 로 정의된 함수에서 선언해줍니다. 다만, 코틀린용으로 라이브러리를 추가하는 게 좋습니다.

testImplementation("org.mockito.kotlin:mockito-kotlin:4.0.0")

when 이 kotlin 의 키워드로 인식되므로, 해당 라이브러리가 지원하는  whenever 를 대신 씁니다.

https://github.com/mockito/mockito-kotlin/wiki/Mocking-and-verifying#stubbing

...
class CalcHandlerTest(
...
) {
    @MockBean
    private lateinit var calculator: CalcService

    @BeforeEach
    private fun init() {
        whenever(calculator.add(1f, 2f)).thenReturn(42f)
    }

근데 굳이 필드에서 바로 초기화 안 하고 @BeforeEach 에서 하는 이유 아 혹시 이게 Bean 이라서? (알아볼 여력은 없고 미궁속으로)

WebMVC 테스트하기

이제 주입받은 MockMvc 로 테스트를 하면 됩니다. 이건 샘플 코드를 보는 게 제일 빠르겠죠.
(org.springframework.test.web.servlet.MockMvc)

@WebMvcTest(CalcHandler::class)
@Import(SayHelloService::class, CalcService::class)
class CalcHandlerTest(
    @Autowired
    private val mockMvc: MockMvc,
    @Autowired
    private val sayHelloService: SayHelloService,
) {
    @MockBean
    private lateinit var calculator: CalcService

    @BeforeEach
    private fun init() {
        whenever(calculator.add(1f, 2f)).thenReturn(42f)
    }

    @Test
    fun test() {
        mockMvc.perform(
            MockMvcRequestBuilders.put("http://localhost:8080/api/v1/calc/add")
                .contentType(MediaType.APPLICATION_JSON)
                .content(ObjectMapper().writeValueAsString(CalcValuesDto(1f, 2f)))
        ).andExpect(
            MockMvcResultMatchers.status().isOk
        ).andExpect(
            MockMvcResultMatchers.jsonPath("$.result").value(42f)
        )
    }
}

이것도 자세하게 알아보고 싶은데 시간이 부족하네요. 오늘은 좀 늦게 시작해서... 주말이라 더 찾아볼 시간이 있긴 했는데; 나중에 필요할 때 알아봐야겠죠 뭐...

테스트 커버리지

저는 IntelliJ 내장 커버리지 기능을 써본 적은 있는데, 강의에서는 Jacoco 라는 라이브러리를 사용합니다. 커버리지야 뭐 워낙 뻔해서...


밤이 늦어가고 있습니다. 한 9시쯤 시작한 거 같은데 몇 시간 그냥 뚝딱이네요. 하긴 테스팅이 제대로 이해하려면 만만한 주제는 아니니까요... 이번에도 어중간하게 넘어가네요.

아 그러고보니 오늘 시간이 많이 간 이유는 IntelliJ 도 한 몫 했네요. IntelliJ 2021.3.2 에서 프로젝트 구조가 제대로 로드가 되질 않았으니; 이전 버전에서는 정상동작했습니다.

본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성되었습니다.
패스트캠퍼스: https://bit.ly/37BpXiC

#패스트캠퍼스 #패캠챌린지 #직장인인강 #직장인자기계발 #패스트캠퍼스후기 #한번에끝내는JavaSpring웹개발마스터초격차패키지Online

뭐 알아봐야할게 자꾸 쌓이네요; 내일은 모닝학습해야하니까 이제 잘 준비를 해야지...

알아볼 거 목록

  • Java @Async
  • Spring 의 구조
  • MockMvc... 는 필요할 때 알아볼래요. 스킵