inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

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

Mockito로 Stubbing하기

Mockito로 Stubbing하기 8분경 OrderRepository 테스트

해결된 질문

147

addvd

작성한 질문수 3

0

안녕하세요 강의를 보다가 OrderRepository에 대한 테스트는 직접 짜보라는 말씀을 듣고 직접 한번 작성을 해봤습니다.

@ActiveProfiles("test")
@DataJpaTest
class OrderRepositoryTest {

    @Autowired
    private OrderRepository orderRepository;

    @Autowired
    private ProductRepository productRepository;

    @DisplayName("원하는 주문 상태를 가진 주문들을 조회한다.")
    @Test
    void findOrdersBy() {
        // given
        Product product1 = createProduct("001", HANDMADE, SELLING, "아메리카노", 4000);
        Product product2 = createProduct("002", HANDMADE, HOLD, "카페라떼", 4500);
        Product product3 = createProduct("003", HANDMADE, STOP_SELLING, "팥빙수", 7000);
        productRepository.saveAll(List.of(product1, product2, product3));

        Order order1 = createOrderWithOrderStatus(List.of(product1, product2), LocalDateTime.now(), PAYMENT_COMPLETED);
        Order order2 = createOrderWithOrderStatus(List.of(product3), LocalDateTime.now().minusDays(1), PAYMENT_COMPLETED);
        Order order3 = createOrderWithOrderStatus(List.of(product1, product2, product3), LocalDateTime.now().plusDays(1), PAYMENT_COMPLETED);
        orderRepository.saveAll(List.of(order1, order2, order3));

        // when
        List<Order> orders = orderRepository.findOrdersBy(
                LocalDateTime.now().minusDays(1),
                LocalDateTime.now().plusDays(1),
                PAYMENT_COMPLETED
        );

        // then
        assertThat(orders).hasSize(2)
                .extracting("orderStatus", "totalPrice", "registeredDateTime")
                .containsExactlyInAnyOrder(
                        tuple(PAYMENT_COMPLETED, 8500, order1.getRegisteredDateTime()),
                        tuple(PAYMENT_COMPLETED, 15500, order3.getRegisteredDateTime())
                );
    }

    private Order createOrder(List<Product> products, LocalDateTime registeredDateTime) {
        return Order.create(products, registeredDateTime);
    }

    private Order createOrderWithOrderStatus(List<Product> products, LocalDateTime registeredDateTime, OrderStatus status) {
        Order order = createOrder(products, registeredDateTime);
        order.updateOrderStatus(status);
        return order;
    }

    private Product createProduct(String productNumber, ProductType productType, ProductSellingStatus sellingStatus, String name, int price) {
        return Product.builder()
                .productNumber(productNumber)
                .type(productType)
                .sellingStatus(sellingStatus)
                .name(name)
                .price(price)
                .build();
    }

}

이런 식으로 작성을 하였는데 제가 궁금한 점은

  1. createProduct는 대부분의 테스트 클래스에서 사용되는데 하나의 static 클래스와 같이 외부로 분리하는게 좋지 않을까 라는 생각을 해봤습니다.

  2. 저는 Order 클래스의 상태 변경을 위해서 updateOrderStatus라는 메소드를 생성 하였는데 (아직 해당 강의를 끝까지는 보지 않고 강의를 보기전에 먼저 테스트 코드를 작성 해보고 있습니다) 이런 식으로 테스트를 위해서 메소드를 생성 하는 경우도 괜찮을까요?

  3. 제가 작성한 코드의 given 절이 너무 길어지는 느낌을 받았습니다. 현재는 order와 product를 생성 하는 정도이지만 더 복잡한 로직을 생성 할 경우 given이 너무 길어 지는것에 대한 해결 방안이 있을까요?

     

최대한 구글을 사용하여 찾아는 보았지만 우빈님의 생각도 궁금하여 여쭤보았습니다!

긴 글 읽어주셔서 감사합니다 🙂

spring tdd jpa mockito 소프트웨어-테스트 junit5

답변 2

0

박우빈

안녕하세요, addvd 님!

 

1. createProduct는 대부분의 테스트 클래스에서 사용되는데 하나의 static 클래스와 같이 외부로 분리하는게 좋지 않을까 라는 생각을 해봤습니다.

강의 중에도 말씀드리겠지만, 물론 그렇게도 할 수 있는데요. test fixture 생성 로직을 통합하여 관리할 경우, 프로젝트의 복잡도가 높아짐에 따라 또다른 관리 대상이 될 수 있습니다. (조금 다르게 kotlin에서는, 메서드에 파라미터명을 지정(named arguments)할 수 있기 때문에 보다 깔끔한 공통 fixture 클래스를 만들 수 있긴 합니다.)

 

2. 저는 Order 클래스의 상태 변경을 위해서 updateOrderStatus라는 메소드를 생성 하였는데 (아직 해당 강의를 끝까지는 보지 않고 강의를 보기전에 먼저 테스트 코드를 작성 해보고 있습니다) 이런 식으로 테스트를 위해서 메소드를 생성 하는 경우도 괜찮을까요?

요것도 뒤쪽 강의에서 말씀 드리겠지만, 테스트를 위한 메서드는 최대한 지양해야 합니다.
그래서 Order 테스트의 경우 Order.create()라는 메서드를 사용하는 대신, 직접 status를 지정할 수 있는 Builder를 사용하여 Order를 생성하는 것이 좋습니다.

 

3. 제가 작성한 코드의 given 절이 너무 길어지는 느낌을 받았습니다. 현재는 order와 product를 생성 하는 정도이지만 더 복잡한 로직을 생성 할 경우 given이 너무 길어 지는것에 대한 해결 방안이 있을까요?

일단 상위 레이어의 테스트일수록, 복잡도가 높을 수록 given절이 길어지는 것은 자연스럽습니다. 상황에 맞게 메서드를 분리하고, 테스트 케이스에 간접적인 영향을 미치는 준비 과정은 BeforeEach를 사용하는 등 여러 방법을 사용해볼 수 있겠습니다. (이것도 뒤쪽 강의에 말씀드려요.)

도움이 되셨기를 바랍니다.
감사합니다. 🙂

 

0

인프런 AI 인턴

안녕하세요, 인프런 AI 인턴입니다.

addvd님께서 직접 작성하신 테스트 코드에 대한 몇 가지 고민에 대해 답변드리겠습니다.

  1. createProduct 분리: 많은 테스트 클래스에서 동일한 createProduct 메소드가 사용된다면 이를 별도의 유틸리티 클래스로 분리하여 static 메소드로 만드는 것은 좋은 선택일 수 있습니다. 이를 통해 코드의 중복을 줄이고 유지 보수를 용이하게 하며, 재사용성을 높일 수 있습니다.

  2. 테스트를 위한 메소드 생성: 테스트를 위해 updateOrderStatus와 같은 메소드를 추가하는 것은 일반적으로 괜찮습니다. 그러나 실제 서비스에 필요하지 않은 메소드라면, 테스트 클래스 내부에 있는 방식으로 접근하거나 별도의 테스트 전용 구성요소를 사용하는 것도 고려할 수 있습니다. 중요한 것은 테스트 코드가 실제 코드에 불필요한 복잡성을 추가하지 않도록 하는 것입니다.

  3. 긴 given 절 단축: given 절이 길어지는 문제는 종종 테스트 코드에서 발생할 수 있는 어려움 중 하나입니다. 이를 해결하기 위해서는 빌더 패턴을 사용하여 객체 생성을 더 간단하고 읽기 쉽게 만들거나, 공통적으로 사용되는 설정을 메소드로 추출하여 간결한 형태로 유지하는 방법이 있습니다. 또한, 복잡한 설정을 하는 부분이 있다면 그 설정 자체를 별도 메소드로 분리하여 given 절에서 호출하는 방식으로 명확성을 높일 수 있습니다.

addvd님께서 작성하신 코드에 대한 분석을 바탕으로 이러한 조언을 드립니다. 더 구체적인 솔루션을 원하시면 다른 전문가 의견도 참고해 보시는 것이 좋겠습니다.

감사합니다! 😊

Spring Boot Reference: Mocking and Spying Beans

private 상수 테스트 관련 질문

0

87

1

void는 어떻게 테스트하나요..? void로 애초에 코딩하면 안되나요??

0

128

2

커버리지는 어떻게 활용하시는지 궁금합니다.

0

163

2

테스트 문서화 질문입니다

0

107

2

단위테스트 질문이 있습니다

0

96

2

컨트롤러는 모킹을 한 이유가 궁금합니다.

0

106

2

ERD 가장자리에 있는 도메인 테스트 질문

0

90

2

DTO 검증 필드에 대한 테스트 코드 작성은 어디까지?

0

136

2

OrderCreateRequest DTO에 대해서 궁금한점

0

104

2

고전파의 테스트 대역 사용 대상, 공유 의존성

0

157

2

계층 관련 질문이 있습니다.

0

140

3

'코틀린'에서는 빌더를 따로 쓰지 않는데, 이 때는 어떻게 test fixture를 만드시는지 궁금합니다

1

124

2

혹시 update 로직은 어떻게 테스트하나요? (@Setter?)

0

135

2

단위테스트와 통합테스트의 경계가 궁금합니다.

0

235

2

Service+Repository 통합테스트 관련 질문입니다.

0

151

2

OrderControllerDocsTest 작성 해봤는데요. 날짜 형식이 이상하게 나와요

0

186

2

test 용 .yml

0

91

2

throws Exception

0

81

2

카페키오스크 클래스 문의 ,,

0

90

2

Rest docs 문서용 테스트코드를 따로 작성해야 되나요?

0

176

2

테스트 코드에서 필요한 생성자

0

139

1

tearDown 순서

0

116

2

@Builder 생성자 private

0

137

2

@DisplayName gradle / intellJ

0

94

2