Spring Boot에서 Testcontainers로 통합 테스트 구성하기
Spring Boot 애플리케이션에서 Testcontainers를 활용해 격리된 데이터베이스 환경으로 통합 테스트를 안정적으로 실행하는 방법과 단계별 예제 코드 모음
목차
개요
통합 테스트는 애플리케이션의 여러 구성요소가 함께 동작하는지 확인하는 핵심 과정이다. 로컬 개발 환경이나 CI에서 데이터베이스, 메시지 큐 같은 외부 의존성을 안전하게 시뮬레이션하려면 Testcontainers가 유용하다. 이 글은 Spring Boot에서 Testcontainers를 적용해 통합 테스트를 구성하는 방법을 예제와 함께 단계별로 설명한다. 초보자도 이해하기 쉽도록 차근차근 진행한다.
필요 조건
- Java 11 이상
- Spring Boot 2.4 이상 권장
- Docker가 설치되어 있고 데몬이 실행 중인 환경
- Gradle 또는 Maven 빌드 도구
Testcontainers 개념 요약
Testcontainers는 Docker 컨테이너를 테스트 환경에서 동적으로 생성하고 관리하는 라이브러리다. 각 테스트는 독립적인 컨테이너를 사용하므로 테스트 간 간섭을 줄인다. 데이터베이스 초기화, 외부 서비스 모킹, 메시지 큐 테스트 등에 유용하다.
프로젝트 설정
아래 예제는 Gradle을 기준으로 한 의존성 설정이다. Spring Boot 테스트와 Testcontainers 관련 라이브러리를 추가한다. 실제 버전은 프로젝트에 맞게 조정한다.
plugins {
id 'org.springframework.boot' version '2.7.5'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
dependencies {
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.testcontainers:junit-jupiter:1.17.6'
testImplementation 'org.testcontainers:postgresql:1.17.6'
runtimeOnly 'org.postgresql:postgresql'
}
Testcontainers 기본 사용법
가장 간단한 형태는 테스트 클래스 내에서 컨테이너를 정적 필드로 선언하는 방식이다. JUnit 5의 @Testcontainers와 @Container를 사용하면 라이프사이클이 자동으로 관리된다.
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
@Testcontainers
public class SimpleContainerTest {
@Container
public static PostgreSQLContainer postgres = new PostgreSQLContainer("postgres:13-alpine")
.withDatabaseName("testdb")
.withUsername("sa")
.withPassword("sa");
@Test
public void contextLoads() {
// 컨테이너가 실행된 상태에서 테스트 로직 수행
}
}
Spring Boot와 연동하기
Spring Boot 테스트에서 Testcontainers를 사용하려면, 실행 중인 컨테이너의 접속 정보를 Spring 프로퍼티로 주입해야 한다. 가장 흔한 방법은 DynamicPropertySource를 이용하는 것이다. 이렇게 하면 DataSource가 컨테이너로 연결된다.
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
@SpringBootTest
@Testcontainers
public class RepositoryIntegrationTest {
@Container
public static PostgreSQLContainer postgres = new PostgreSQLContainer("postgres:13-alpine")
.withDatabaseName("testdb")
.withUsername("sa")
.withPassword("sa");
@DynamicPropertySource
static void registerProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username", postgres::getUsername);
registry.add("spring.datasource.password", postgres::getPassword);
}
@Test
void repositoryTest() {
// 실제 리포지토리 호출로 DB 연동 검증
}
}
실전 예제: CRUD 검증 흐름
통합 테스트는 보통 데이터 초기화와 검증 단계로 구성된다. 아래 흐름을 권장한다.
- 컨테이너 기동
- 스키마 마이그레이션(예: Flyway, Liquibase) 적용
- 테스트 데이터 삽입
- 비즈니스 로직 호출 및 검증
- 컨테이너 종료 및 자원 정리
Flyway 같은 마이그레이션 도구를 사용하면 애플리케이션의 실제 스키마와 동일한 환경에서 테스트할 수 있다. CI 환경에서는 Docker 데몬이 필수다.
주의사항과 팁
- 컨테이너 기동 시간으로 인해 테스트가 느려질 수 있으니, 통합 테스트는 적절히 그룹화한다.
- 같은 컨테이너를 여러 테스트에서 공유할 경우 상태 격리를 고려한다.
- CI 환경에서는 도커 리소스(메모리, CPU)를 충분히 할당한다.
- 로컬에서 빠른 반복 실행이 필요하면 Mock 기반 단위 테스트와 병행한다.
정리
Testcontainers는 Spring Boot 통합 테스트에서 외부 의존성을 현실적으로 시뮬레이션하는 강력한 도구다. 위 예제처럼 컨테이너를 설정하고 DynamicPropertySource로 프로퍼티를 주입하면 실제 데이터베이스와 동일한 환경에서 테스트할 수 있다. 통합 테스트를 안정적으로 운영하면 회귀를 조기에 발견하고 배포 품질을 높일 수 있다.