H2 대신 PostgreSQL로 검증하는 이유
Repository 테스트가 H2에서는 통과하고 PostgreSQL에서는 깨지는 일은 보통 타입, 예약어, SQL 방언 차이에서 온다. 쿼리와 매핑을 운영 DB에 가깝게 확인하려면 테스트 DB도 PostgreSQL로 맞추면 낫다.
Testcontainers는 테스트 시작 시 Docker 컨테이너를 띄우고 종료 시 정리한다. Spring Boot 3.1 이상에서는 spring-boot-testcontainers와 @ServiceConnection을 함께 써서 컨테이너 접속 정보를 DataSource에 자동 반영할 수 있다.
테스트 의존성은 test 스코프에 둔다
운영 코드에는 영향을 주지 않도록 spring-boot-testcontainers, org.testcontainers:postgresql, PostgreSQL JDBC 드라이버를 테스트 의존성으로만 둔다. datasource 값을 억지로 덮기보다 테스트 클래스에서 DB 생명주기를 선언하면 의도가 분명하다.
JPA 슬라이스에 컨테이너 붙이기
Repository만 확인한다면 전체 애플리케이션을 띄우지 말고 @DataJpaTest로 범위를 좁힌다.
@DataJpaTest
@Testcontainers
@AutoConfigureTestDatabase(replace = Replace.NONE)
class MemberRepositoryTest {
@Container
@ServiceConnection
static PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>("postgres:16-alpine");
@Autowired MemberRepository memberRepository;
@Test
void savesMember() {
Member saved = memberRepository.save(new Member("spring"));
assertThat(memberRepository.findById(saved.getId())).isPresent();
}
}
replace = NONE은 슬라이스 테스트가 임베디드 DB로 바꾸지 못하게 막는다. @Container는 컨테이너 생명주기를 JUnit에 맡기고, @ServiceConnection은 Spring Boot가 PostgreSQL 컨테이너에서 JDBC 연결 정보를 만들도록 연결한다.
스키마 검증을 테스트에 포함하기
컨테이너가 떴다고 충분한 것은 아니다. 마이그레이션을 쓰는 프로젝트라면 테스트도 같은 경로로 스키마를 만들게 두어야 테이블 정의 차이를 놓치지 않는다. 테스트 프로필에서도 spring.jpa.hibernate.ddl-auto=validate와 Flyway 실행 여부를 확인한다.
간단한 저장 검증에는 @Sql로 seed 데이터를 넣어도 된다. 중요한 기준은 Docker 데몬 미실행, 이미지 pull 실패, 마이그레이션 오류를 테스트가 숨기지 않는 것이다. CI에서는 Docker 사용 가능 여부도 확인한다.
느린 테스트 경계를 정하기
Testcontainers 테스트는 단위 테스트보다 느리다. SQL 방언, 인덱스, 제약 조건, 매핑처럼 실제 DB 차이가 의미 있는 경계에만 둔다. 순수 서비스 로직은 단위 테스트로 남기면 피드백 속도를 잃지 않는다.
같은 Spring 테스트 컨텍스트를 공유하면 시작 비용을 줄일 수 있지만, 클래스마다 속성을 바꾸면 캐시가 갈라질 수 있다. 공통 컨테이너 설정은 작게 유지하고 테스트 데이터는 각 테스트에서 준비한다.
댓글
댓글 쓰기