[스프링 활용] 주문 서비스 개발 + 테스트

atdawn·2024년 6월 10일

SPRING BOOT+JPA

목록 보기
26/49

참고 : 인프런 [ 실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발 - 김영한 ]


public class OrderService {

    private final OrderRepository orderRepository;
    private final MemberRepository memberRepository;
    private final ItemRepository itemRepository;

    /**
     * 주문
     */
    @Transactional
    public Long order(Long memberId,Long itemId, int count){

        //엔티티 조회
        Member member = memberRepository.findOne(memberId);
        Item item= itemRepository.findOne(itemId);

        //배송정보 설정
        Delivery delivery=new Delivery();
        delivery.setAddress(member.getAddress());

        //주문상품 생성
        OrderItem orderItem = OrderItem.createOrderItem(item, item.getPrice(), count);

        //주문 생성
        Order order = Order.createOrder(member, delivery, orderItem);

        //주문 저장
        orderRepository.save(order);

        return order.getId();

    }
  • orderItem 과 delivery를 각각 repository를 사용하여 save한 후 createOrder에 넘겨주지 않고, createOrder에 order만 save하였다.
    • 엔티티를 설계할때 delivery와 orderItem은 casecade타입을 ALL 로 설정했기 때문에 order가 persist되면 orderItem과 delivery가 자동으로 persist 된다.
    • casecade All의 경우 여러 엔티티에서 참조하는 엔티티라면 사용하지 않아야 한다. (라이프사이클 고려)

      @NoArgsConstructor(access = AccessLevel.PROTECTED) : 롬복
      생성 메서드를 사용하지 않고 new OrderItem(); 처럼 객체를 생성한후 Setter을 사용하여 객체를 각각 세팅하는 방법을 사용하게 되면, 추후 유지보수가 어려워진다.
      이를 해결하기 위해 @NoArgsConstructor(access = AccessLevel.PROTECTED) 어노테이션을 사용하여,
      객체를 직접 생성하는 것을 방지한다.

 /**
     * 주문 취소
     */
    @Transactional
    public void cancelOrder(Long orderId){
        //엔티티 조회
        Order order = orderRepository.findOne(orderId);
        //주문 취소
        order.cancle();
    }
  • JPA를 사용하면, 주문 취소, 주문수량 변경과 같은 로직을 바꾸고 나서 쿼리를 또다시 날려줘야하는 번거러움을 덜어준다.
  • JPA는 엔티티 안에 있는 데이터들만 변경해주면, Dirty Checking 이라는 변경 포인트 감지가 일어나면서 데이트베이스에 업데이트 쿼리를 자동으로 날리게 된다.

참고: 주문 서비스의 주문과 주문 취소 메서드를 보면 비즈니스 로직 대부분이 엔티티에 있다. 서비스 계층은 단순 히 엔티티에 필요한 요청을 위임하는 역할을 한다.


주문 테스트

상품주문

...
@Test
    public void 상품주문() throws Exception {
        //given
        Member member = createMember();

        Book book = createBook("시골 JPA", 10000, 10);

        int orderCount=2;

        //when
        Long orderId = orderService.order(member.getId(), book.getId(), orderCount);

        //then
        Order getOrder = orderRepository.findOne(orderId);
        assertEquals("상품 주문시 상태는 ORDER", OrderStatus.ORDER,getOrder.getStatus());
        assertEquals("주문한 상품 종류 수가 정확해야 한다.",1,getOrder.getOrderItems().size());
        assertEquals("주문 가격은 가격 * 수량이다", 10000 * orderCount, getOrder.getTotalPrice());
        assertEquals("주문 수량 만큼 재고가 줄어야 한다.",8,book.getStockQuantity());


    }
 private Book createBook(String name, int price, int stockQuantity) {
        Book book=new Book();
        book.setName(name);
        book.setPrice(price);
        book.setStockQuantity(stockQuantity);
        em.persist(book);
        return book;
    }

    private Member createMember() {
        Member member=new Member();
        member.setName("회원1");
        member.setAddress(new Address("서울","강가","123-123"));
        em.persist(member);
        return member;
    }
    ...
    
  • 원활한 테스트를 위해 createMember(), createBook()은 메서드로 정의한다.

    상품주문 재고수량초과

 ...
  @Test(expected = NotEnoughStockException.class) //익셉션이 발생해야 테스트 통과
    public void 상품주문_재고수량초과() throws Exception {
        //given
        Member member=createMember();

        Item item = createBook("시골 JPA", 10000, 10);

        int orderCount=11;

        //when
        orderService.order(member.getId(), item.getId(), orderCount);


        //then
        fail("재고 수량 부족 예외가 발생해야 한다.");

    }
   ...
  • 익셉션이 발생하지 않으면 fail() 메서드를 만나 테스트가 실패한다.

주문 취소

@Test
    public void 주문취소() throws Exception {
        //given
        Member member=createMember();
        Book item = createBook("시골 JPA", 10000, 10);

        int ordercount=2;
        Long orderId = orderService.order(member.getId(), item.getId(), ordercount);

        //when
        orderService.cancelOrder(orderId); //취소

        //then
        Order getOrderId = orderRepository.findOne(orderId);

        assertEquals("주문 취소시 상태는 CANCEL 이다.", OrderStatus.CANCLE, getOrderId.getStatus());
        assertEquals("주문이 취소된 상품은 그만큼 재고가 증가되야 한다.", 10, item.getStockQuantity());
        
    }

통합테스트 결과 모두 성공한 것을 확인하였다!

profile
복습 복습 복습

0개의 댓글