package codej.todo_list.demo.service.impl;
import codej.todo_list.demo.todo.dto.ProductResponseDto;
import codej.todo_list.demo.todo.entity.ProductEntity;
import codej.todo_list.demo.todo.repository.ProductRepository;
import codej.todo_list.demo.todo.service.ProductServiceImpl;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.verify;
import static org.mockito.AdditionalAnswers.returnsFirstArg;
public class ProductServiceTest {
private ProductRepository productRepository = Mockito.mock(ProductRepository.class);
private ProductServiceImpl productService;
@BeforeEach
public void setUpTest() {
productService = new ProductServiceImpl(productRepository);
}
@Test
void getProductTest() {
ProductEntity givenProduct = new ProductEntity();
givenProduct.setPno(123L);
givenProduct.setName("펜");
givenProduct.setPrice(1000);
givenProduct.setStock(5555);
Mockito.when(productRepository.findById(123L))
.thenReturn(Optional.of(givenProduct));
ProductResponseDto productResponseDto = productService.getProduct(123L);
assertEquals(productResponseDto.getPno(),givenProduct.getPno());
assertEquals(productResponseDto.getName(),givenProduct.getName());
assertEquals(productResponseDto.getPrice(),givenProduct.getPrice());
assertEquals(productResponseDto.getStock(),givenProduct.getStock());
verify(productRepository).findById(123L);
}
}
@Test
void saveProductTest() {
Mockito.when(productRepository.save(any(ProductEntity.class)))
.then(returnsFirstArg());
ProductResponseDto productResponseDto = productService.setProduct(new ProductDto("펜", 4000, 55));
assertEquals(productResponseDto.getName(),"펜");
assertEquals(productResponseDto.getPrice(),4000);
assertEquals(productResponseDto.getStock(),55);
verify(productRepository).save(any());
}
}
이 예제에서 살펴볼 내용은 any() 입니다.
any(Product.class)로 동작을 설정 하였는데 , 일반적으로 given()으로 정의된 Mock 객체의 메서드 동작 감지는 매개변수의 비교를 통해 이뤄지나 래퍼런스 변수의 비교는 주소값으로 이루어지기 때문에 any()를 사용하여 클래스만 정의하는 경우도 있습니다.
지금까지 소개한 테스트는 Mock 객체를 활용한 방식이였습니다. 큰 차이는 없지만 Mock 객체를 직접 생성하지않고 @MockBean 어노테이션을 사용해 스프링 컨테이너에 Mock객체를 주입받는 방식을 소개하겠습니다.
package codej.todo_list.demo.service.impl;
import codej.todo_list.demo.todo.repository.ProductRepository;
import codej.todo_list.demo.todo.service.ProductService;
import codej.todo_list.demo.todo.service.ProductServiceImpl;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ExtendWith(SpringExtension.class)
@Import({ProductServiceImpl.class})
public class ProductServiceTest2 {
@MockBean
ProductRepository productRepository;
@Autowired
ProductService productService;
}
동작을 설정하는 ProductRepository에 대한 초기화 작업을 어떻게 진행하는지를 비교하기 위한 코드입니다.
위의 예제에서는 Mockito를 통해 Repository를 Mock 객체로 대체하는 작업을 수행하고 서비스 객체를 직접 초기화했습니다.
반면 이번 예제에서는 스프링에서 제공하는 테스트 어노테이션을 통해 Mock 객체를 생성하고 의존성을 주입 받고 있습니다.
스프링에서 객체를 주입받기 위해 @ExtendWith(SpringExtension.class)를 사용해 JUnit5의 테스트에서 스프링 테스트 컨텍스트를 사용하도록 설정합니다.