안녕하세요 TIL 첫번째 시간입니다.
2023/06/07
배운것을 정리해보겠습니다 👨💻
Spring Test Context
에 대해 알아보기 전에 Spring Application Context
에 대해 알아봅시다. 자세히 알고 싶다면 여기를 참고해주세요 ❗️
Application Context 는 Spring 어플리케이션이 구동시에 생성되는 IoC의 엔진역할을 하는 컨테이너 입니다. Bean 들을 생성하고 관리하는 Bean Factory
를 상속하여 확장한 개념으로 application 의 구성 요소(Bean, 설정 파일) 등을 관리하고, 의존성 주입을 처리합니다.
Spring Test Context는 Spring Application의 테스트 환경을 구성하는데에 사용됩니다. 쉽게 말해 Test 버전의 Application Context라고 이해하면 됩니다.
Test에 필요한 Bean을 관리하고, 테스트 환경을 설정합니다.
일반적으로 Test 시에는 Spring Test Context가 Application Context를 확장하여 필요한 설정과 테스트용 Bean을 추가하거나 변경합니다.
Test에서만 사용되는 특정 Bean 들을 관리하는 컨테이너이기에 격리된 Test 환경을 구성합니다 🔥
두 어노테이션은 Spring + Java 를 이용해 프로젝트나 실습을 진행할 때 테스트를 설계하는 경우 마주치는 어노테이션입니다.
Presentation Layer
을 테스트 하는 경우에는 @WebMvc
+ @MockBean
을 사용하며 Service Layer
를 테스트 하는 경우에는 @Mock
+ InjectMocks
을 사용합니다.
지금까지는 그냥 그려러니 하고 사용했지만 이번 기회에 두 어노테이션의 동작 원리와 왜 사용하는 경우가 다른지 알아보겠습니다 🔥
@MockBean
은 Spring 에서 기본으로 제공하는 어노테이션입니다.
org.springframework.boot.test.mock.mockito
패키지에 정의되어 있습니다.
MockBean
은 해당하는 모의 객체를 Test context 에서 실제 Bean으로 등록합니다. 이렇게 등록이 되면 의존성 주입이 필요한 곳에 Spring 이 주입시켜 줍니다.
Controller Test 의 특징을 곰곰히 생각해봅시다.
Controller 는 HTTP 요청을 처리하고, 클라이언트와의 인터페이스 역할을 합니다.
이러한 Controller 를 Test 하기 위해 Controller Test 를 구성 시 실제로 요청을 보내는 것이 아니라, 모의 객체를 통해 Http 응답 동작을 검증합니다.
따라서 외부 의존성(ex. Service & Repository)을 Mock 객체로 대체할 필요가 있는데 해당 Mock 객체들은 Spring의 Test context 에 등록이 되어 의존성이 필요한 곳에 주입됩니다.
결론적으로, @MockBean
을 이용해서 Controller 에 필요한 외부 의존성을 Mock 객체로 대체하며 이를 통해 Controller 의 동작을 격리시킬 수 있습니다.
여기서 잠깐 이런 궁금중이 생길 수 있습니다.
Controller Test 시 굳이
@MockBean
을 사용해야할까? 어차피 동일한 모의객체를 만들껀데@Mock
을 사용하면 되는거 아닐까?
Controller Test에는 @MockBean
을 사용해야하는 이유는 실제 환경과 비슷하게 하기 위함입니다.
즉, 실제 application 에서 서로 상호작용하는 Controller,Service,Repository를 실제 환경과 유사하게 context 에 등록하여 테스트를 진행하면 실제 요청과 응답을 처리하는 동작을 테스트할 수 있으며, 실제 DB 와의 상호 작용도 확인할 수 있습니다❗️
또한 웹 계층과 관련된 빈들을 제외하고 모두 모의 객체이기에 실제 반영이 되지 않습니다.(Test 내용 DB 반영
정리하자면 실제 환경과 유사한 테스트를 수행하기 위해 Controller Test에 @MockBean
을 사용합니다 🤖
@MockBean
까지 알아본 김에 같이 사용되는 @WebMvcTest
에 대해서도 파헤쳐봅시다 🔥
@WebMvcTest는 Spring MVC 기반의 웹 어플리케이션을 테스트하기 위한 어노테이션입니다. 이 어노테이션을 통해 특정 Controller 나 Controller 계층을 대상으로 하는 테스트를 작성할 수 있습니다.
@WebMvcTest
를 사용하면 전체 Spring application context 에서 일부만 로드하여 test context를 구성합니다.
여기서 로드되는 Bean 들로는 WebMvcConfigurer
,Controller
,Filter
등이 있습니다. 그 외에 Service
,Repository
등 외부 Bean은 test context 에 등록되지 않습니다.
이렇게 웹 계층(Presentation Layer) 에서만 필요한 Bean 만 등록 후, MockMvc
객체를 사용해서 테스트 요청을 보내고, 결과를 검증합니다 👩🔬
사실 Controller 는 Service Layer 및 Repository Layer 등의 외부 Layer에 의존해있습니다.
따라서 여기서 앞서 살펴보았던 @MockBean
을 통해 외부 의존성을 Mock 객체로 대체합니다.
@Mock
은 @MockBean
과 달리 Spring 에서 기본적으로 제공되지 않습니다.
해당 어노테이션은 Mockito
혹은 Mockk(Kotlin)
과 같은 Mocking 프레임워크와 함께 사용되며 Mocking 프레임워크와 관련된 의존성을 프로젝트에 추가해야 사용가능합니다.
org.mockito.Mock
io.mockk.MockK
@MockBean
처럼 특정 Bean 을 Mock 객체로 대체하는 것이 아니라, 해당 Mock 객체를 직접 생성하여 사용한다. -> 의존성 주입 x@Mock
과 함께 사용되는 @InjectMocks
는 @Mock
으로 만들어진 모의 객체가 주입될 대상(여기서는 Service) 에 선언되는 어노테이션입니다.
@InjectMocks
를 통해 Service 객체에 모의 객체들이 주입되며 Service 객체는 주입된 모의 객체를 사용하여 테스트를 수행할 수 있습니다🔥
개인적으로 이부분에 대해서 많이 찾아보고 했지만 깔끔한 결론을 내리기는 어려웠습니다.
하지만 제가 어느정도 이해한 내용을 토대로 포스팅을 이어가보겠습니다 👨💻
@MockBean
의 특징에 대해서 다시 한번 생각해볼까요?
@MockBean
은 Test context에 대상 Bean 을 대체하는 Mock 객체를 등록한다. 따라서 실제 의존성과 유사한 상호작용을 보장한다.
이와 같은 @MockBean
의 특징과 함께 @MockBean도 Service Test에 사용 가능할까?
에 대한 대답은 YES 입니다.
하지만, Service Layer는 보통 다른 Service Layer 및 Repository Layer 에만 의존합니다. 따라서 복잡한 테스트 환경이 요구되지 않습니다.
하지만 @MockBean
을 사용하는 경우 실제 의존성을 대체하기 위해 모의 객체를 등록해야 하며 실제 동작과 상호 작용을 완전히 대체해야합니다.
또한 테스트 횐경이 복잡해 질 수 있습니다. 이는 Bean을 모의 객체로 대체해야하기 때문이며, 이를 관리해야하기 때문입니다. 특히 프로젝트가 커지고 의존성이 복잡해지면 관리가 어려워집니다 😥
이러한 점들을 고려해보았을 때 Service Test 에서는 @MockBean
을 사용하기 보다는 사용자가 직접 정의하는 가짜 모의 객체를 @Mock
을 통해 생성하는 것이 더 효율적입니다 ❗️
하지만 간혹, Service Test에서 실제와 같은 상호작용이 필요한 경우 @MockBean
이 좋은 방안이 될 수 있습니다.
따라서 목적과 포커스를 고려해서 적절한 방식을 선택하는 것이 중요하지만 일반적으로는 Service Test 에는 @Mock
을 사용합니다 🐯