1. 비즈니스 로직 설명
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
OrderRepository orderRepository;
@Autowired
public OrderServiceImpl(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
@Override
public OrderDto createOrder(OrderDto orderDto) {
ModelMapper mapper = new ModelMapper();
mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
OrderEntity orderEntity = mapper.map(orderDto, OrderEntity.class);
return mapper.map(orderRepository.save(orderEntity), OrderDto.class);
}
OrderService의 createOrder 함수와 연관된 비즈니스 로직 테스트를 진행하고자 한다.
간단하게 살펴보면 OrderRepository에 의존하여 DB에 Order 정보를 저장하는 로직이다.
createOrder 메소드를 보면 orderRepository.save(orderEntity) 를 통해 Repository에 데이터를 저장하고 Entity를 반환해오는 것을 알 수 있다.
public interface OrderRepository extends CrudRepository<OrderEntity, Long> {
OrderEntity findByOrderId(String orderId);
Iterable<OrderEntity> findByUserId(String userId);
}
JPA를 사용하지않는 분들을 위해 참고로 설명드리면, OrderRepository는 기본적인 CRUD기능을 제공하는 CrudRepository 인터페이스를 상속받고 있기 때문에 save함수를 따로 작성하지 않아도 된다.
2. 테스트 관련 설명
public class TestInfoFixture {
public static final String ORDER_ID = "orderId";
public static final String USER_ID = "userId";
public static final String PRODUCT_ID = "productId";
public static final int QTY = 10;
public static final int UNIT_PRICE = 10000;
}
TestInfoFixture 클래스는 테스트를 유지보수를 용이하게 하기 위해서 만든 테스트 픽스쳐 클래스다.
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Mock
private OrderRepository orderRepository;
@InjectMocks
private OrderServiceImpl orderService;
private OrderDto orderDto;
private OrderEntity orderEntity;
@BeforeEach
void setUp() {
orderDto = OrderDto.builder()
.orderId(ORDER_ID)
.productId(PRODUCT_ID)
.qty(QTY)
.unitPrice(UNIT_PRICE)
.totalPrice(QTY * UNIT_PRICE)
.build();
ModelMapper mapper = new ModelMapper();
mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
orderEntity = mapper.map(orderDto, OrderEntity.class);
}
@Test
@DisplayName("주문정보 생성 성공")
void createOrder() {
// given
given(orderRepository.save(any())).willReturn(orderEntity);
// when
final OrderDto result = orderService.createOrder(orderDto);
// then
verify(orderRepository).save(any());
assertThat(result.getOrderId()).isNotEmpty();
assertThat(result.getTotalPrice()).isEqualTo(result.getUnitPrice() * result.getQty());
}
Service 레이어의 단위 테스트를 진행할 때는, 모든 빈이 등록될 필요도 없고 실제로 스프링 컨테이너에 올라갈 필요도 없다.
때문에 @SpringBootTest 대신에 @ExtendWith(MockitoExtensions.class)를 사용하면 Spring에 의존하지 않고 테스트 범위에 해당하는 OrderService 객체만 실제로 생성되어 빠른 테스트를 제공할 수 있다.
mock객체를 통해 OrderService에 해당하는 테스트 범위에 맞게 최소한의 자원만 사용하면 된다.
// given
given(orderRepository.save(any())).willReturn(orderEntity);
먼저 @Mock을 통해 OrderRepository의 mock객체를 생성해준다. 그리고 given 영역에서 orderRepository.save함수가 호출되면 미리 생성해둔 orderEntity를 반환할 수 있도록 스터빙한다.
@InjectMocks
private OrderServiceImpl orderService;
// when
final OrderDto result = orderService.createOrder(orderDto);
그리고 when 영역에서 orderService의 createOrder함수를 실제로 호출해줘야하는데 orderService에 orderRepository가 의존해 있기 때문에, 가짜 의존성을 주입해주기 위해서 @InjectMocks을 사용한다.
// then
verify(orderRepository).save(any());
assertThat(result.getOrderId()).isNotEmpty();
assertThat(result.getTotalPrice()).isEqualTo(result.getUnitPrice() * result.getQty());
마지막으로 then영역에서 orderRepository.save 함수가 동작하는지 검증하고 assertThat을 통해 기대값과 결과값을 비교한다.
'Spring Framework.' 카테고리의 다른 글
DI & IoC의 개념정리 (0) | 2022.09.14 |
---|---|
SpringBootTest vs WebMvcTest (0) | 2022.02.06 |
Mockito vs BDDMockito (0) | 2022.02.04 |
비대칭키를 이용한 Config Server 암호화/복호화 기능 구현 (0) | 2022.01.29 |