도메인 테스트 코드 작성을 해봤습니다.
도메인
//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번 방법으로, 일부 중복된 검증이 발생하더라도 객체마다 단위 테스트 코드를 작성하여 해당 행위의 정확한 의도를 검증하고 표현할 것 같습니다.
도움이 되셨기를 바라요.
감사합니다. :)
0
!! Mock으로 단위테스트를 실행한다는 것만 기억하고
제어하기 어려운걸 제어하는 용도로 사용하라고 말씀하신걸 잊고있었습니다.
답변 감사합니다 . 좋은 하루 보내세요!:)
private 상수 테스트 관련 질문
0
83
1
void는 어떻게 테스트하나요..? void로 애초에 코딩하면 안되나요??
0
126
2
커버리지는 어떻게 활용하시는지 궁금합니다.
0
161
2
테스트 문서화 질문입니다
0
105
2
단위테스트 질문이 있습니다
0
96
2
컨트롤러는 모킹을 한 이유가 궁금합니다.
0
103
2
ERD 가장자리에 있는 도메인 테스트 질문
0
89
2
DTO 검증 필드에 대한 테스트 코드 작성은 어디까지?
0
135
2
OrderCreateRequest DTO에 대해서 궁금한점
0
102
2
고전파의 테스트 대역 사용 대상, 공유 의존성
0
156
2
계층 관련 질문이 있습니다.
0
139
3
'코틀린'에서는 빌더를 따로 쓰지 않는데, 이 때는 어떻게 test fixture를 만드시는지 궁금합니다
1
124
2
혹시 update 로직은 어떻게 테스트하나요? (@Setter?)
0
135
2
단위테스트와 통합테스트의 경계가 궁금합니다.
0
229
2
Service+Repository 통합테스트 관련 질문입니다.
0
150
2
OrderControllerDocsTest 작성 해봤는데요. 날짜 형식이 이상하게 나와요
0
185
2
test 용 .yml
0
90
2
throws Exception
0
80
2
카페키오스크 클래스 문의 ,,
0
89
2
Rest docs 문서용 테스트코드를 따로 작성해야 되나요?
0
174
2
테스트 코드에서 필요한 생성자
0
138
1
tearDown 순서
0
116
2
@Builder 생성자 private
0
136
2
@DisplayName gradle / intellJ
0
93
2





