[JPA] 주문 기능 테스트

이준영·2022년 11월 1일
0

스프링 - JPA

목록 보기
10/11

주문 도메인 개발한 것을 테스트해보자

테스트 코드의 기본적인 형태는 다음과 같다.

@Test
public void  throws Exception{
    //given
    
    //when
    
    //then
    
}

이전포스트에서 해당 코드를 단축어(tdd)로 설정하는 작업을 했는데
이번에 또 써먹으니까 완전 유용하다!!

이제 무엇을 테스트 해봐야 되는지 알아보자
1. 상품 주문이 성공해야 한다.
2. 상품 주문 시 재고 수량을 초과하면 안된다.
3. 주문 취소가 성공해야 한다.

@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class OrderServiceTest {

    @Autowired EntityManager em;
    @Autowired OrderService orderService;
    @Autowired OrderRepository orderRepository;

    @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());
    }

    @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("재고 수량 부족 예외가 발생해야 한다.");

    }
    @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 getOrder = orderRepository.findOne(orderId);

        assertEquals("주문 취소시 상태는 CANCEL 이다.", OrderStatus.CANCEL, getOrder.getStatus());
        assertEquals("주문이 취소된 상품은 그만큼 재고가 증가히야 한다.", 10, item.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;
    }

}
  • 상품주문
    Given : 테스트를 위한 회원과 상품을 만든다
    When : 실제 상품을 주문
    Then : 주문 가격이 올바른지, 주문 후 재고 수량이 정확히 줄었 는지 검증

  • 재고 수량 초과
    @Test(expected = NotEnoughStockException.class)로 예외 발생
    Given : 상품 주문
    When : 주문 취소
    Then : 주문상태가 주문취소 상태인지, 취소한 만큼의 재고가 증가했는지 확인


주문 검색 기능

JPA에서 동적 쿼리를 어떻게 해결해야 하는가?

OrderRepository에 검색 기능을 추가해주어야 한다.
findAll__(OrderSearch orderSearch) 메서드는 검색 조건에 동적으로 쿼리를 생성해서 주문 엔티티를 조회하는 함수이다.

  • findAll( ) 메서드를 만드는 세가지 방법이 있다.
  1. JPQL - 번거롭고, 실수로 인해 오류 발생 가능성 높다
  2. JPA Criteria - JPA 표준스펙 but, 실무에서 사용하기 복잡하다
  3. Querydsl - 코드가 엄청 간결해진다!!

그래서 Querydsl 을 쓰는게 좋은데 이번에는 이런게 있다고만 알아두고 JPA 표준 스펙인 JPA Criteria 코드를 사용하고 넘어가자.

*JPA Criteria에 대한 자세한 내용은 자바 ORM 표준 JPA 프로그래밍 책을 참고하자

package jpabook.jpashop.repository;

@Repository
@RequiredArgsConstructor
public class OrderRepository {

    @PersistenceContext
    private final EntityManager em;

    public void save(Order order) {em.persist(order);}

    public Order findOne(Long id) {return em.find(Order.class, id);}
    

    // 검색기능 - 동적쿼리
    /**
     * JPQL
     */
    public List<Order> findAllByString(OrderSearch orderSearch) {
        String jpql = "select o From Order o join o.member m";
        boolean isFirstCondition = true;

        //주문 상태 검색
        if (orderSearch.getOrderStatus() != null) {
            if (isFirstCondition) {
                jpql += " where";
                isFirstCondition = false;
            } else {
                jpql += " and";
            }
            jpql += " o.status = :status";
        }
        //회원 이름 검색
        if (StringUtils.hasText(orderSearch.getMemberName())) {
            if (isFirstCondition) {
                jpql += " where";
                isFirstCondition = false;
            } else {
                jpql += " and";
            }
            jpql += " m.name like :name";
        }

        TypedQuery<Order> query = em.createQuery(jpql, Order.class) .setMaxResults(1000); //최대 1000건
        if (orderSearch.getOrderStatus() != null) {
            query = query.setParameter("status", orderSearch.getOrderStatus());
        }
        if (StringUtils.hasText(orderSearch.getMemberName())) {
            query = query.setParameter("name", orderSearch.getMemberName());
        }

        return query.getResultList();
    }

    /**
     * JPA Criteria : JPA 표준 스펙.. 근데 유지보수가 안됨
     * 그냥 이런식으로 할 수 있다는 정도
     */
    public List<Order> findAllByCriteria(OrderSearch orderSearch) {
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Order> cq = cb.createQuery(Order.class);
        Root<Order> o = cq.from(Order.class);
        Join<Order, Member> m = o.join("member", JoinType.INNER); //회원과 조인
        List<Predicate> criteria = new ArrayList<>();

        //주문 상태 검색
        if (orderSearch.getOrderStatus() != null) {
            Predicate status = cb.equal(o.get("status"),
                    orderSearch.getOrderStatus());
            criteria.add(status);
        }

        //회원 이름 검색
        if (StringUtils.hasText(orderSearch.getMemberName())) {
            Predicate name =
                    cb.like(m.<String>get("name"), "%" +
                            orderSearch.getMemberName() + "%");
            criteria.add(name);
        }
        cq.where(cb.and(criteria.toArray(new Predicate[criteria.size()])));
        TypedQuery<Order> query = em.createQuery(cq).setMaxResults(1000); //최대 1000건
        return query.getResultList();
    }


/*
    // Query DSL
    public List<Order> findAll(OrderSearch orderSearch) {

        QOrder order = Qorder.order;
        QMember member = QMember.member;

        return query
                .select(order)
                .from(order)
                .join(order.member, member)
                .where(statusEq(orderSearch.getOrderStatus()),
                        nameLike(orderSearch.getMemberName()))
                .limit(1000)
                .fetch();
    }
*/


}
profile
화이팅!

0개의 댓글