문제발생
관리자가 원하는 날짜의 주문판매현황을 조회한 후 이메일로 전송받는 로직의 테스트코드이며 테스트 메서드 자체에는 문제가 없지만 테스트의 독립성을 위해 테스트 후 deleteAllInBatch() 메서드를 통해 저장소를 비우는 과정에서 예외가 발생한듯 하였다.
@SpringBootTest
class OrderStatisticsServiceTest {
@Autowired
OrderStatisticsService orderStatisticsService;
@Autowired
OrderRepository orderRepository;
@Autowired
ProductRepository productRepository;
@AfterEach
void tearDown() {
orderRepository.deleteAllInBatch();
productRepository.deleteAllInBatch();
}
@Test
@DisplayName("결제완료 주문들을 조회하여 매출 통계 메일을 전송한다.")
void sendOrderStatisticsMail() {
// given
LocalDateTime now = LocalDateTime.of(2023, 3, 5, 10, 0);
Product product1 = createProduct("001", HANDMADE, 1000);
Product product2 = createProduct("002", HANDMADE, 2000);
Product product3 = createProduct("003", HANDMADE, 3000);
List<Product> products = List.of(product1, product2, product3);
productRepository.saveAll(products);
Order order1 = createPaymentCompletedOrder(products, now);
Order order2 = createPaymentCompletedOrder(products, LocalDateTime.of(2023, 3, 5, 23, 59, 59));
Order order3 = createPaymentCompletedOrder(products, LocalDateTime.of(2023, 3, 4, 23, 59, 59));
Order order4 = createPaymentCompletedOrder(products, LocalDateTime.of(2023, 3, 6, 0, 0, 0));
//when
boolean result = orderStatisticsService.sendOrderStatisticsMail(LocalDate.of(2023, 3, 5), "test@test.com");
//then
assertThat(result).isTrue();
}
private Order createPaymentCompletedOrder(List<Product> products, LocalDateTime orderDate) {
Order order = Order.builder()
.products(products)
.orderStatus(OrderStatus.COMPLETED)
.registeredDateTime(orderDate)
.build();
return orderRepository.save(order);
}
private Product createProduct(String productNumber, ProductType type, int price) {
return Product.builder()
.productNumber(productNumber)
.type(type)
.price(price)
.build();
}
예외 메세지는 아래와 같다
JDBC exception executing SQL [delete from orders o1_0] [Referential integrity constraint violation: "FKL5MNJ9N0DI7K1V90YXNTHKC73: PUBLIC.ORDER_PRODUCT FOREIGN KEY(ORDER_ID) REFERENCES PUBLIC.ORDERS(ID) (CAST(1 AS BIGINT))"; SQL statement:
delete from orders o1_0 [23503-224]] [n/a]; SQL [n/a]; constraint [FKL5MNJ9N0DI7K1V90YXNTHKC73: PUBLIC.ORDER_PRODUCT FOREIGN KEY(ORDER_ID) REFERENCES PUBLIC.ORDERS(ID) (CAST(1 AS BIGINT)); SQL statement:
원인
예외 메시지에서 "Referential integrity constratin violation"은 참조 무결성 제약 조건 위반을 의미하는 데이터베이스 예외이다
즉 어떤 테이블의 데이터를 삭제하고자할 때 해당 데이터가 다른 테이블에서 참조되고 있기때문에 삭제가 불가능 하다는 의미이다
프로젝트의 구조가 orders테이블이 order_product에 참조되고 있기때문에 orders의 데이터를 삭제하기위해서는 order_product에 데이터가 먼저 삭제되야 하는 것으로 보인다
해결
tearDown() 메서드의 동작순서를 @AfterEach가 아니라 @BeforeEach로 설정하니 테스트코드가 정상적으로 실행되었다
@AfterEach를 사용하나 @BeforeEach를 사용하나 똑같이 참조 무결성 제약조건을 위반하여 삭제가 안될거라 생각했는데
@AfterEach는 모든 테스트가 끝난 후에 모든 데이터를 삭제하려고 시도하기때문에 이미 실행된 테스트에의해 각각의 테이블에 데이터가 남아있기때문에 부모 테이블의 데이터를 삭제하려고 시도하니 예외가 발생하는 것이다.
@BeforeEach를 사용하게되면 테스트 메서드가 실행되기전에 데이터베이스를 초기화하기때문에 테스트 실행전에 데이터베이스 자체를 초기화 시키기때문에 제약조건을 위반하지 않게 되는 것이다.
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!