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

goforit님의 프로필 이미지
goforit

작성한 질문수

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

Mockito로 Stubbing하기

OrderRepositoryTest에서 발생한 에러

작성

·

455

0

package sample.cafekiosk.spring.domain.order;


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

    @Autowired
    private OrderRepository orderRepository;

    @Autowired
    private ProductRepository productRepository;

    @DisplayName("특정 주문 상태에 따라 주문을 조회한다")
    @Test
    void findOrdersBy() {
        //given
        Product product1 = createProduct("아메리카노", 3000, HANDMADE, SELLING);
        Product product2 = createProduct("카페라떼", 4000, HANDMADE, SELLING);
        Product product3 = createProduct("카푸치노", 5000, HANDMADE, SELLING);
        List<Product> products = List.of(product1, product2, product3);

        LocalDateTime startTime = LocalDateTime.of(2023, 10, 19, 0, 0);
        LocalDateTime orderTime = LocalDateTime.of(2023, 10, 19, 10, 0);
        LocalDateTime endTime = LocalDateTime.of(2023, 10, 20, 0, 0);

        Order completedOrder = createOrder(orderTime, PAYMENT_COMPLETED, products);
        Order canceledOrder = createOrder(orderTime, CANCELED, products);

        // when
        List<Order> orders = orderRepository.findOrdersBy(startTime, endTime, PAYMENT_COMPLETED);

        // then
        assertThat(orders).hasSize(1)
                .extracting("id", "orderStatus", "totalPrice", "registeredDateTime")
                .containsExactlyInAnyOrder(
                        tuple(1L, PAYMENT_COMPLETED, 12000, orderTime)
                );
    }

    private Product createProduct(String name, int price, ProductType productType, ProductSellingStatus productSellingStatus) {
        Product product = Product.builder()
                .name(name)
                .price(price)
                .type(productType)
                .sellingStatus(productSellingStatus)
                .build();
        return productRepository.save(product);
    }

    private Order createOrder(LocalDateTime now, OrderStatus orderStatus, List<Product> products) {
        Order order = Order.builder()
                .products(products)
                .orderStatus(orderStatus)
                .registeredDateTime(now)
                .build();
        return orderRepository.save(order);
    }

    @DisplayName("찾고자 하는 시간 안에 있는 주문을 조회한다")
    @Test
    void findOrdersBy2() {
        //given
        Product product1 = createProduct("아메리카노", 3000, HANDMADE, SELLING);
        Product product2 = createProduct("카페라떼", 4000, HANDMADE, SELLING);
        Product product3 = createProduct("카푸치노", 5000, HANDMADE, SELLING);
        List<Product> products = List.of(product1, product2, product3);

        LocalDateTime startTime = LocalDateTime.of(2023, 10, 19, 0, 0);
        LocalDateTime orderTime = LocalDateTime.of(2023, 10, 19, 10, 0);
        LocalDateTime endTime = LocalDateTime.of(2023, 10, 20, 0, 0);
        LocalDateTime overTime = LocalDateTime.of(2023, 10, 20, 10, 0);

        Order completedOrder = createOrder(orderTime, PAYMENT_COMPLETED, products);
        Order overTimeOrder = createOrder(overTime, PAYMENT_COMPLETED, products);

        // when
        List<Order> orders = orderRepository.findOrdersBy(startTime, endTime, PAYMENT_COMPLETED);

        // then
        assertThat(orders).hasSize(1)
                .extracting("id", "orderStatus", "totalPrice", "registeredDateTime")
                .containsExactlyInAnyOrder(
                        tuple(1L, PAYMENT_COMPLETED, 12000, orderTime)
                );
    }

}

각각 Test 수행할 땐 정상적으로 잘 동작했습니다.

 

하지만, 같이 Test을 수행하는 경우 findOrdersBy()에서 아래와 같은 에러가 발생하고 있습니다

2023-10-20 23:12:40.519  INFO 8704 --- [           main] o.s.t.c.transaction.TransactionContext   : Began transaction (1) for test context [DefaultTestContext@1a6c1270 testClass = OrderRepositoryTest, testInstance = sample.cafekiosk.spring.domain.order.OrderRepositoryTest@2d114d27, testMethod = findOrdersBy@OrderRepositoryTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@18a136ac testClass = OrderRepositoryTest, locations = '{}', classes = '{class sample.cafekiosk.spring.CafeKioskApplication}', contextInitializerClasses = '[]', activeProfiles = '{test}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory$DisableAutoConfigurationContextCustomizer@560348e6, org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@6f1c29b7, org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizer@351584c0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@fb58afcf, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@7b7fdc8, [ImportsContextCustomizer@77d67cf3 key = [org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, eJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@27d5a580, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@0], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.event.ApplicationEventsTestExecutionListener.recordApplicationEvents' -> false]]; transaction manager [org.springframework.orm.jpa.JpaTransactionManager@40d10264]; rollback [true]
Hibernate: 
    insert 
    into
        product
        (id, created_date_time, modified_date_time, name, price, product_number, selling_status, type) 
    values
        (default, ?, ?, ?, ?, ?, ?, ?)
Hibernate: 
    insert 
    into
        product
        (id, created_date_time, modified_date_time, name, price, product_number, selling_status, type) 
    values
        (default, ?, ?, ?, ?, ?, ?, ?)
Hibernate: 
    insert 
    into
        product
        (id, created_date_time, modified_date_time, name, price, product_number, selling_status, type) 
    values
        (default, ?, ?, ?, ?, ?, ?, ?)
Hibernate: 
    insert 
    into
        orders
        (id, created_date_time, modified_date_time, order_status, registered_date_time, total_price) 
    values
        (default, ?, ?, ?, ?, ?)
Hibernate: 
    insert 
    into
        order_product
        (id, created_date_time, modified_date_time, order_id, product_id) 
    values
        (default, ?, ?, ?, ?)
Hibernate: 
    insert 
    into
        order_product
        (id, created_date_time, modified_date_time, order_id, product_id) 
    values
        (default, ?, ?, ?, ?)
Hibernate: 
    insert 
    into
        order_product
        (id, created_date_time, modified_date_time, order_id, product_id) 
    values
        (default, ?, ?, ?, ?)
Hibernate: 
    insert 
    into
        orders
        (id, created_date_time, modified_date_time, order_status, registered_date_time, total_price) 
    values
        (default, ?, ?, ?, ?, ?)
Hibernate: 
    insert 
    into
        order_product
        (id, created_date_time, modified_date_time, order_id, product_id) 
    values
        (default, ?, ?, ?, ?)
Hibernate: 
    insert 
    into
        order_product
        (id, created_date_time, modified_date_time, order_id, product_id) 
    values
        (default, ?, ?, ?, ?)
Hibernate: 
    insert 
    into
        order_product
        (id, created_date_time, modified_date_time, order_id, product_id) 
    values
        (default, ?, ?, ?, ?)
Hibernate: 
    select
        order0_.id as id1_2_,
        order0_.created_date_time as created_2_2_,
        order0_.modified_date_time as modified3_2_,
        order0_.order_status as order_st4_2_,
        order0_.registered_date_time as register5_2_,
        order0_.total_price as total_pr6_2_ 
    from
        orders order0_ 
    where
        order0_.registered_date_time>=? 
        and order0_.registered_date_time<? 
        and order0_.order_status=?
2023-10-20 23:12:40.621  INFO 8704 --- [           main] o.s.t.c.transaction.TransactionContext   : Rolled back transaction for test: [DefaultTestContext@1a6c1270 testClass = OrderRepositoryTest, testInstance = sample.cafekiosk.spring.domain.order.OrderRepositoryTest@2d114d27, testMethod = findOrdersBy@OrderRepositoryTest, testException = java.lang.AssertionError: [Extracted: id, orderStatus, totalPrice, registeredDateTime] 
Expecting actual:
  [(3L, PAYMENT_COMPLETED, 12000, 2023-10-19T10:00 (java.time.LocalDateTime))]
to contain exactly in any order:
  [(1L, PAYMENT_COMPLETED, 12000, 2023-10-19T10:00 (java.time.LocalDateTime))]
elements not found:
  [(1L, PAYMENT_COMPLETED, 12000, 2023-10-19T10:00 (java.time.LocalDateTime))]
and elements not expected:
  [(3L, PAYMENT_COMPLETED, 12000, 2023-10-19T10:00 (java.time.LocalDateTime))]
, mergedContextConfiguration = [MergedContextConfiguration@18a136ac testClass = OrderRepositoryTest, locations = '{}', classes = '{class sample.cafekiosk.spring.CafeKioskApplication}', contextInitializerClasses = '[]', activeProfiles = '{test}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper=true}', contextCustomizers = 

왜 id가 3인가요?? 저는 DataJpaTest을 수행하면 각각의 Test 마다 rollBack이 수행되어 id가 당연히 1이라고 생각했었습니다. 왜 3이 되는지 이해가 되지 않습니다

 

 

답변 2

0

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

안녕하세요, goforit 님! :)

데이터가 롤백되는 것은 맞으나, 데이터베이스에서 Auto-increment로 증가시켜주고 있는 ID 값은 데이터베이스에서 관리하는 값으로 롤백되는 것이 아닙니다. 선형으로 증가하는 값이죠.
테스트의 경우 저희가 인메모리 DB인 H2를 사용하고 있기 때문에, 통합테스트를 실행하는 시점에 H2 DB가 실행되고 해당 테이블의 ID 값은 1부터 시작합니다.
그러나 그 이후로는 Insert 작업이 일어날 때마다 롤백 여부와 관계 없이 선형으로 증가하게 됩니다.

그래서 테스트에서도 ID 값이 몇 번인지를 숫자로 매칭해서 검증하기 보다는, given 절에서 생성한 fixture(completedOrder)의 ID 값과 같은지를 확인하는 방법을 사용해보면 좋을 것 같아요.

//given

...

Order completedOrder = createOrder(orderTime, PAYMENT_COMPLETED, products);
Order canceledOrder = createOrder(orderTime, CANCELED, products);

// when
List<Order> orders = orderRepository.findOrdersBy(startTime, endTime, PAYMENT_COMPLETED);

// then
assertThat(orders).hasSize(1)
        .extracting("id", "orderStatus", "totalPrice", "registeredDateTime")
        .containsExactlyInAnyOrder(
                tuple(completedOrder.getId(), PAYMENT_COMPLETED, 12000, orderTime)
        );

감사합니다. :)

0

안녕하세요 goforit 님, 인프런 AI 인턴이에요.

에러 메세지를 보니, OrderRepositoryTest 클래스에서 수행한 테스트 중 findOrdersBy() 메서드에서 오류가 발생한 것으로 보입니다. 오류 메시지에는 “Extracted: id, orderStatus, totalPrice, registeredDateTime” 와 “elements not found” 등의 정보가 나와있습니다.

id가 3인 이유에 대해서는 코드 상황을 직접 확인해야 정확한 판단을 내릴 수 있을 것 같습니다. 하지만, 주어진 코드에서는 createProduct() 메서드와 createOrder() 메서드를 통해 각각의 상품과 주문을 생성하고 있습니다. 이 메서드들에 의해 데이터베이스에 저장되는 순서와 상관없이 id가 부여되는 경우가 있을 수 있습니다.

더 자세한 원인을 알기 위해서는 데이터베이스 저장 및 조회 과정에 대한 설정 및 코드를 확인해야 할 것 같습니다. 데이터베이스 트랜잭션과 롤백에 관련된 설정, 그리고 데이터베이스의 데이터 초기화 과정 등을 확인해 보시면 도움이 될 수 있을 것입니다.

추가로, 테스트 케이스가 동일한 데이터를 사용하고 있는지, 각 테스트 케이스에서 데이터를 정리하고 초기화하는 작업이 필요한지도 고려해 보시기 바랍니다. 이를 통해 각 테스트 케이스에서 독립적인 상황을 구성할 수 있을 것입니다.

더 자세한 도움을 드리기 위해서는 코드의 전체 내용과 환경 설정에 대한 정보가 필요할 수 있습니다. 이런 경우에는 팀 내 개발자나 원활한 커뮤니케이션을 할 수 있는 인프런 고객센터로 문의하시는 것이 좋을 것입니다. 감사합니다.

goforit님의 프로필 이미지
goforit

작성한 질문수

질문하기