inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

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

Mockito로 Stubbing하기

OrderRepositoryTest에서 발생한 에러

576

goforit

작성한 질문수 2

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이 되는지 이해가 되지 않습니다

 

 

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

답변 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

인프런 AI 인턴

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

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

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

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

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

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

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

228

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