DB와의 상호작용이 필요한 테스트코드는 어떻게 작성할까
주문 생성 API가 재고와 배송 상태를 제대로 바꿨는지 검증하려고, Database Rider로 인메모리 DB에 데이터셋을 심고 기대 데이터셋과 비교하는 방식으로 통합 테스트를 짠 기록.
작성날짜: 24.09.12
상황
- 주문 생성 API 에서 데이터 상태값 검증 필요
- ex. 재고 수량 업데이트, 배송 상태 …
- 상용 데이터베이스와 별개로 테스트를 진행하고 싶음
Database Rider 라이브러리를 사용해보자
- 준비한 데이터를 기반으로 서비스 검증 가능
- 인메모리 데이터베이스 사용
특징
- 데이터셋 관리: YAML, JSON, XML 등 다양한 형식으로 테스트 데이터 정의
- 데이터베이스 시딩: 테스트 실행 전에 데이터베이스에 필요한 데이터를 쉽게 삽입
- 데이터 검증: 테스트 후 데이터베이스 상태를 쉽게 확인
- JUnit 통합: JUnit과 원활하게 통합되어 테스트 프로세스를 자동화
- 다양한 데이터베이스 지원: 여러 종류의 관계형 데이터베이스와 호환
- 트랜잭션 관리: 테스트 간 데이터 격리를 보장
테스트 데이터셋 준비하기
- 테스트 기능 위에 준비된 파일 어노테이션 준비
준비한 테스트 데이터셋으로 테스트하기
- 테스트 데이터 셋은 데이터베이스에 자동 준비
- 기대값 데이터 셋은 변경된 데이터베이스 내 데이터와 비교 검증 진행
예시 코드
@DbUnit(driver = "org.h2.Driver", url = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1")
@ExtendWith(DBUnitExtension::class)
@SpringBootTest
class OrderServiceIntegrationTest {
@Autowired
lateinit var orderService: OrderService
@Test
@DataSet("datasets/orders.yml")
@ExpectedDataSet("datasets/expected-orders.yml")
fun `주문 처리 후 상태가 변경되어야 한다`() {
val result = orderService.processOrder(1L)
assertEquals("PROCESSED", result.status)
}
@Test
@DataSet("datasets/orders.yml")
fun `존재하지 않는 주문에 대해 예외가 발생해야 한다`() {
assertThrows<OrderNotFoundException> {
orderService.processOrder(999L)
}
}
}
검증 프로세스 요약
- 데이터베이스 상태 검증
@ExpectedDataSet - 예상 데이터 셋 이용
- 데이터베이스 검증 프로세스
- 테스트 메소드 실행 전: @DataSet에 정의된 초기 데이터가 데이터베이스에 로드
- 테스트 메소드 실행: orderService.processOrder(1L)가 호출되어 주문 상태를 변경
- 테스트 메소드 실행 후: Database Rider가 자동으로 데이터베이스의 현재 상태와 @ExpectedDataSet에 정의된 예상 상태를 비교
- 종합적 검증
- 검증 범위 선정
위와 같은 테스트 진행 이점은?
- 서비스 메소드의 반환값 뿐 아니라 데이터베이스의 실제 상태 변화까지 포괄적 검증 가능
- 데이터의 일관성 + 비즈니스 로직의 정확성 보장!
- 서비스 계층 + 데이터 접근 계층 전체 로직의 정확성 검증 가능
회고
- 주문 생성 API 테스트에 사용해보자