인프런 커뮤니티 질문&답변

kamser님의 프로필 이미지
kamser

작성한 질문수

Practical Testing: 실용적인 테스트 가이드

도메인 테스트 코드 작성을 해봤습니다.

해결된 질문

작성

·

784

·

수정됨

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 객체를 만드니까 간단한 단위테스트 인데도 속도가 많이 느려지더라구요

이런 경우에

  1. Order.cancle 로직에 대한 테스트이기 때문에 나머지는 Mock으로 처리

    1. 관련된 OrderItem.cancle(),Item.addStock()은 별도 단위테스트로 검증했기때문에

      추가 검증이 필요없다.

  2. Order.cancle 로직에 대한 테스트이지만,

    1. OrderItem.cancle(),Item.addStock() 테스트 검증이 끝났다고 해도

      비즈니스 계층 통합테스트 느낌으로 given에서 데이터를 준비하고

    2. 연관된 데이터를 전부 검증한다.

       

1번으로 할경우 테스트 목적은 한눈에 들어오지만

2번으로 할경우 이 로직이 어디까지 관련이 되어있는지 테스트 코드로 알수 있을거같아서

강사님이 말씀하신 테스트를 문서처럼 사용할 수 있는거같습니다.

 

고민을 해보니 Order.cancel()은 서비스 계층에서 호출을 할텐데

1번으로 테스트를 하고, 서비스 계층에서 통합 테스트를 하는게 맞을까요?

더 나은 방법이 있을까요?

 

답변 1

2

박우빈님의 프로필 이미지
박우빈
지식공유자

안녕하세요, kamser님! :)
답변 드리겠습니다.

강의 중에도 말씀드린 것처럼, mocking은 기본적으로는 지양하되 외부 네트워크 요청처럼 제어하기 어려운 행위를 제어하기 위한 용도로 사용하는 것이 좋습니다.

저라면 말씀주신 방법 중 2번 방법으로, 일부 중복된 검증이 발생하더라도 객체마다 단위 테스트 코드를 작성하여 해당 행위의 정확한 의도를 검증하고 표현할 것 같습니다.

도움이 되셨기를 바라요.
감사합니다. :)

kamser님의 프로필 이미지
kamser
질문자

!! Mock으로 단위테스트를 실행한다는 것만 기억하고

제어하기 어려운걸 제어하는 용도로 사용하라고 말씀하신걸 잊고있었습니다.

답변 감사합니다 . 좋은 하루 보내세요!:)

 

kamser님의 프로필 이미지
kamser

작성한 질문수

질문하기