16.3 Mockito를 이용한 서비스 레이어 슬라이스 테스트
단위 테스트에서 실제 DB나 외부 API 연결 없이 계층 간 의존성을 끊어주는 도구가 Mockito입니다.
1. @Mock과 @InjectMocks
@ExtendWith(MockitoExtension.class) // JUnit5에서 Mockito를 사용하는 핵심 설정
class ProductServiceTest {
@Mock
private ProductRepository productRepository; // 가짜(Mock) 객체 생성
@Mock
private CacheManager cacheManager;
@InjectMocks
private ProductService productService; // Mocks를 자동으로 주입 받음
@Test
@DisplayName("상품 조회 시 캐시 히트가 없으면 DB에서 조회한다.")
void 상품_조회_DB() {
// Given: Mock의 행동 정의
Long productId = 1L;
Product mockProduct = new Product(productId, "스프링 교재", 35000);
given(productRepository.findById(productId))
.willReturn(Optional.of(mockProduct));
// When: 실제 메서드 실행
ProductDto result = productService.getProduct(productId);
// Then: 결과 검증
assertThat(result.getName()).isEqualTo("스프링 교재");
// Mock 호출 횟수 검증 (정확히 1번 호출됐는지)
then(productRepository).should(times(1)).findById(productId);
}
@Test
@DisplayName("존재하지 않는 상품 조회 시 예외가 발생한다.")
void 상품_없음_예외() {
given(productRepository.findById(anyLong())).willReturn(Optional.empty());
assertThatThrownBy(() -> productService.getProduct(999L))
.isInstanceOf(ProductNotFoundException.class);
}
}
2. ArgumentCaptor — 메서드에 전달된 인자 캡처
Mock이 호출될 때 어떤 인자가 넘어왔는지 검증합니다.
@Test
@DisplayName("주문 생성 시 이벤트 발행 메서드에 올바른 데이터가 전달된다.")
void 주문_생성_이벤트_검증() {
// Given
ArgumentCaptor<OrderCreatedEvent> captor = ArgumentCaptor.forClass(OrderCreatedEvent.class);
// When
orderService.createOrder(new CreateOrderRequest(userId, productId, quantity));
// Then: 이벤트 발행 메서드에 넘어간 인자를 캡처해서 검증
then(eventPublisher).should().publishEvent(captor.capture());
assertThat(captor.getValue().getUserId()).isEqualTo(userId);
assertThat(captor.getValue().getQuantity()).isEqualTo(quantity);
}