앞서 작성했던 테스트는 일명 통합 테스트(Integration Test)라고 한다.
👉🏻 2개 이상의 클래스(혹은 컴포넌트)가 협력하는 테스트, 일반적으로 스프링 부트 애플리케이션을 실행시켜서 두 클래스에 대한 빈을 실제로 생성하여 테스트한다.
→ SimpleProductService를 테스트하기 위해서 ProductRepository 인터페이스의 구현체 중 하나를 Profile에 따라 빈을 생성하여 SimpleProductService를 테스트하는 데 사용
👉🏻 '내가 개발 중인 코드' 외에도 '내가 개발 중인 코드가 의존하고 있는 코드'까지 대상으로 실행한다
⚠️ 이를 위해서는 ProductRepository에 대한 구현체가 반드시 필요하다는 문제가 생긴다. 실무에서는 특정 인터페이스에 대한 구현체가 없는 상태에서 개발하고 있는 로직을 테스트해야 하는 경우가 종종 발생한다.
다른 클래스를 사용하지 않고 작동하는 테스트
→ 개발 중인 로직만 간결하고 빠르게 테스트하기 위해 실행한다.
👉🏻 이럴 때 사용할 수 있는 기법이 바로 '모킹(Mocking)'이다!
자바에서는 모킹 라이브러리로 'Mockito'를 많이 사용한다.
SimpleProductServiceUnitTest.java
package kr.co.hanbit.product.management.application;
import kr.co.hanbit.product.management.domain.Product;
import kr.co.hanbit.product.management.domain.ProductRepository;
import kr.co.hanbit.product.management.presentation.ProductDto;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
public class SimpleProductServiceUnitTest {
@Mock
private ProductRepository productRepository;
@Mock
private ValidationService validationService;
@InjectMocks
private SimpleProductService simpleProductService;
@Test
@DisplayName("상품 추가 후에는 추가된 상품이 반환되어야한다.")
void productAddTest() {
ProductDto productDto = new ProductDto("연필", 300, 200);
Long PRODUCT_ID = 1L;
Product product =ProductDto.toEntity(productDto);
product.setId(PRODUCT_ID);
when(productRepository.add(any())).thenReturn(product);
ProductDto savedProductDto = simpleProductService.add(productDto);
assertTrue(savedProductDto.getId().equals(PRODUCT_ID));
assertTrue(savedProductDto.getName().equals(productDto.getName()));
assertTrue(savedProductDto.getPrice().equals(productDto.getPrice()));
assertTrue(savedProductDto.getAmount().equals(productDto.getAmount()));
}
}
새로운 테스트 코드 파일을 추가한다. (기존 테스트 코드와 동일한 패키지)
@ExtendWith(MockitoExtension.class)
→ 스프링 부트 애플리케이션을 실행시키지 않고도 테스트 코드를 실행시킬 수 있다 @SpringBootTest
와 차이: 스프링 부트 애플리케이션이 뜨느냐 마느냐를 결정 @SpringBootTest
→ 스프링 부트 애플리케이션이 시작되면서 필요한 의존성들을 빈으로 등록하고 주입하는 과정을 거친다 @ActiveProfiles
지정 X → ProductRepository 구현체 없이 테스트 할 수 있는 단위 테스트를 할 것이기 때문에 필요가 없다@Mock
→ 해당 의존성에 '목 객체(Mock Object)'를 주입한다는 의미로 테스트 코드에서 목 객체가 어떤 메서드에 대해 어떤 동작을 할지는 스스로 정의해야 함@InjectMock
→ 위에서 @Mock으로 주입해 준 목 객체들을 SimpleProductService 내에 있는 의존성에 주입해 주는 역할, 목 객체가 아니라 실제 인스턴스를 생성하여 로직으로 사용할 수 있다when(productRepository.add(any())).thenReturn(product);
→ 목 객체가 어떻게 행동해야 하는지 정의when
: 목 객체가 when에 해당하는 동작을 수행할 때any()
: 아무 값이나 들어간다는 의미 thenReturn
: thenReturn에 있는 값을 반환한다