오늘 볼 강의는 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() { } }
- 강의에서는 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... 는 필요할 때 알아볼래요. 스킵
'FastCampus - 한번에 끝내는 Java|Spring 웹 개발 > 03 스프링 입문' 카테고리의 다른 글
스프링 Ch 10 맛집 - memory DB - 패스트캠퍼스 챌린지 16일차 (0) | 2022.02.08 |
---|---|
스프링 Ch 9 Swagger/OpenAPI - 패스트캠퍼스 챌린지 15일차 (0) | 2022.02.07 |
스프링 Ch 7-3 네이버 검색 연동 / JUnit - 패스트캠퍼스 챌린지 13일차 (0) | 2022.02.05 |
스프링 Ch 7 Spring as client - 패스트캠퍼스 챌린지 12일차 (0) | 2022.02.04 |
스프링 Ch 6-5 Filter / Interceptor - 패스트캠퍼스 챌린지 11일차 (0) | 2022.02.03 |