인프런 커뮤니티 질문&답변
도메인 테스트 코드 작성을 해봤습니다.
해결된 질문
작성
·
921
·
수정됨
0
- 도메인 
//Order 도메인
@Entity
@Table(name = "orders")
@Getter @Setter
@NoArgsConstructor
public class Order {
    @Enumerated(EnumType.STRING)
    private OrderStatus status;
    @OneToMany(mappedBy = "order",cascade = CascadeType.ALL)
    private List<OrderItem> orderItems = new ArrayList<>();
    /* 주문 취소 */
    public void cancel(){
        this.setStatus(OrderStatus.CANCEL);
        /** 재고 원복 */
        for (OrderItem orderItem : orderItems) {
            orderItem.cancel();
        }
    }
}//OrderItem
public class OrderItem {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "item_id")
    private Item item;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "order_id")
    private Order order;
    public void cancel() {
        getItem().addStock(count); // 재고수량을 늘려준다.
    }
}//Item
public class Item {
    private int stockQuantity;
    public void addStock(int quantity) {
        this.stockQuantity += quantity;
    }
}불필요한 코드는 제거 했습니다.
- 테스트 코드 
@DisplayName("주문을 생성하고, 주문 취소시 주문 취소로 상태를 변경하고, 
                               감소된 상품 재고를 수량을 추가한다.")
@Test
void cancelOrder(){
   //given
   OrderItem orderItemMock1 = Mockito.mock(OrderItem.class);
   OrderItem orderItemMock2 = Mockito.mock(OrderItem.class);
   BDDMockito.doNothing().when(orderItemMock1).cancel();
   BDDMockito.doNothing().when(orderItemMock2).cancel();
   //OrderItem 가변인자는 내부에서 List로 보관
   Order order = Order.createOrder(orderItemMock1,orderItemMock2);
   //when
   order.cancel();
   //then
   assertThat(order.getStatus()).isEqualByComparingTo(OrderStatus.CANCEL);
   Mockito.verify(orderItemMock1,Mockito.times(1)).cancel();
   Mockito.verify(orderItemMock2,Mockito.times(1)).cancel();
}Order 도메인의 cancel()을 테스트 코드를 작성해야한다고 할때
cancel 호출로 변경된 상태(enum)와
내부 List<OrderItem> 필드에 하나씩 취소 요청을 할때
Mock으로 OrderItem 도메인을 만들고 호출을 했는지만 검사를 했습니다.
Mock 객체를 만드니까 간단한 단위테스트 인데도 속도가 많이 느려지더라구요

이런 경우에
- Order.cancle 로직에 대한 테스트이기 때문에 나머지는 Mock으로 처리 - 관련된 OrderItem.cancle(),Item.addStock()은 별도 단위테스트로 검증했기때문에 - 추가 검증이 필요없다. 
 
- Order.cancle 로직에 대한 테스트이지만, - OrderItem.cancle(),Item.addStock() 테스트 검증이 끝났다고 해도 - 비즈니스 계층 통합테스트 느낌으로 given에서 데이터를 준비하고 
- 연관된 데이터를 전부 검증한다. 
 
1번으로 할경우 테스트 목적은 한눈에 들어오지만
2번으로 할경우 이 로직이 어디까지 관련이 되어있는지 테스트 코드로 알수 있을거같아서
강사님이 말씀하신 테스트를 문서처럼 사용할 수 있는거같습니다.
고민을 해보니 Order.cancel()은 서비스 계층에서 호출을 할텐데
1번으로 테스트를 하고, 서비스 계층에서 통합 테스트를 하는게 맞을까요?
더 나은 방법이 있을까요?
답변 1
2
안녕하세요, kamser님! :)
답변 드리겠습니다.
강의 중에도 말씀드린 것처럼, mocking은 기본적으로는 지양하되 외부 네트워크 요청처럼 제어하기 어려운 행위를 제어하기 위한 용도로 사용하는 것이 좋습니다.
저라면 말씀주신 방법 중 2번 방법으로, 일부 중복된 검증이 발생하더라도 객체마다 단위 테스트 코드를 작성하여 해당 행위의 정확한 의도를 검증하고 표현할 것 같습니다.
도움이 되셨기를 바라요.
감사합니다. :)







!! Mock으로 단위테스트를 실행한다는 것만 기억하고
제어하기 어려운걸 제어하는 용도로 사용하라고 말씀하신걸 잊고있었습니다.
답변 감사합니다 . 좋은 하루 보내세요!:)