아무 의존성을 주입받지 않은 상태에서 단위 테스트를 작성하였을 때
public class ProductServiceTest {
private ProductRepository productRepository = Mockito.mock(ProductRepository.class);
private ProductServiceImpl productService;
@BeforeEach
public void setUpTest(){
productService = new ProductServiceImpl(productRepository);
}
@Test
void getProductTest(){
Product givenProduct = new Product();
givenProduct.setNumber(123L);
givenProduct.setName("펜");
givenProduct.setPrice(1000);
givenProduct.setStock(1234);
Mockito.when(productRepository.findById(123L))
.thenReturn(Optional.of(givenProduct));
ProductResponseDto productResponseDto = productService.getProduct(123L);
Assertions.assertEquals(productResponseDto.getNumber(), givenProduct.getNumber());
Assertions.assertEquals(productResponseDto.getName(), givenProduct.getName());
Assertions.assertEquals(productResponseDto.getPrice(), givenProduct.getPrice());
Assertions.assertEquals(productResponseDto.getStock(), givenProduct.getStock());
verify(productRepository).findById(123L);
}
}
@Test
void saveProductTest(){
Mockito.when(productRepository.save(any(Product.class)))
.then(returnsFirstArg());
ProductResponseDto productResponseDto = productService.saveProduct(
new ProductDto("펜", 1000, 1234));
Assertions.assertEquals(productResponseDto.getName(), "펜");
Assertions.assertEquals(productResponseDto.getPrice(), 1000);
Assertions.assertEquals(productResponseDto.getStock(), 1234);
verify(productRepository).save(any());
}
any()는 Mockito의 ArgumentMatchers에서 제공하는 메서드로 Mock 객체의 동작을 정의하거나 검증하는 단계에서 조건으로 특정 매개변수의 전달을 설정하지 않고 메서드의 실행만을 확인하거나 좀 더 큰 범위의 클래스 객체를 매개변수로 전달받는 등의 상황에 사용
일반적으로 given()으로 정의된 Mock 객체의 메서드 동작 감지는 매개변수의 비교를 통해 이뤄지나, 레퍼런스 변수의 비교는 주솟값으로 이뤄지기 때문에 any()를 사용해 클래스만 정의하는 경우도 있음
Mock객체를 직접 생성하지 않고 @MockBean 어노테이션을 사용해 스프링 컨테이너에 Mock객체를 주입받는 방법
@ExtendsWith(SpringExtension.class)
@Import({ProductServiceImpl.class})
class ProductServiceTest2{
@MockBean
ProductRepository productRepository
@Autowired
ProductService productService
테스트 코드 생략
}
이 전에는 Mockito를 통해 리포지토리를 Mock객체로 대체하는 작업을 수행하고 서비스 객체를 직접 초기화하였음
반면 위에서는 Test 어노테이션을 통해 Mock 객체를 생성하고 의존성을 주입받고 있음
둘의 차이 -> 스프링의 기능에 의존을 하는지 안하는지
두예제 다 Mock객체를 활용한 테스트 방식인 것은 동일하나 @MockBean
을 사용하는 방식은 스프링에 Mock객체를 직접 주입받는 방법이며 Mockito.mock()
을 사용하는 방식은 스프링 빈에 등록하지 않고 직접 객체를 초기화해서 사용하는 방식
보통은 mock객체를 직접 생성하는 것이 더 빠름
스프링에서 객체를 주입받기 위해 ExtendsWith(SpringExtension.class)
를 사용해 JUnit5의 테스트에서 스프링 테스트 컨텍스트를 사용하도록 설정
이후 @Autowired 어노테이션으로 주입받는 ProductService
를 주입받기 위해 직접 클래스를 @Import 어노테이션을 사용