TestContainers를 이용한 테스트 환경 구성 공부/BE, DB
Date 2022. 3. 1. 17:25Test 환경 구성 방법
테스트 환경을 구성하기 위해서는 다양한 방법이 존재한다.
대표적으로 Spring Boot는 Data Access 관련 테스트를 위해 H2 database(in-memory db)를 자동으로 Configuration 해준다.
많이 쓰이는 다른 방법으로는 바로 Docker
를 이용하는 방법이 있는데, 컨테이너들을 띄워 테스트를 하기 위해 필요한 의존관계(대표적으로 데이터베이스, 다른 dependent 서비스들 포함)을 편리하게 설정해줄 수 있다.
또다른 방법으로는 글에서 소개할 TestContainers 를 이용하는 방법이 있다.
TestContainers란 ?
도커를 이용하여 테스트 환경을 구성해줄 수 있는 라이브러리다. 기존에 도커를 이용해 테스트 환경을 구성한다 하면, 보통 docker-compose
을 이용해 구성할 수 있는데 이는 테스트 환경 구성을 스프링 외부에서 해야한다는 단점이 존재한다.
반면 TestContainers
를 이용한다면 코드 레벨에서의 컨테이너 설정, inter-container 간의 네트워크 설정 등을 할 수 있다.
TestContainers의 장점
위에서 언급한 코드 레벨에서의 컨테이너 설정 외에도 다양한 장점이 존재한다.
대표적으로 로그 관련으로, 컨테이너의 로그를 Spring Process로 파이프할 수 있다.
즉 테스트 환경에서 Applciation Log와 Container Log를 함께 보며 디버깅 할 수 있다는 장점이 있다.
또한 Docker에 의존성이 존재하지만, 로컬에 있는 도커에 연결(docker.sock) 뿐만 아니라 tcp를 통해서 외부에 있는 도커 서버와도 연결해 사용할 수 있다.
그리고 구체적인 Container
클래스를 제공한다. 대표적으로는 JdbcDatabaseContainer
로 객체가 jdbcUrl
등의 멤버들을 제공한다.
Configuration : 1. dependency 설정
예제 환경은 Spring Boot(2.6.2), Kotlin(1.6.10), Gradle Kotlin DSL을 이용해 진행하였다.
다음과 같은 디펜던시가 필요하다. build.gradle.kts
에 다음 의존성을 추가한다. 여기서는 MySQL
을 이용했다.
testImplementation("org.testcontainers:junit-jupiter:1.16.3")
testImplementation("org.testcontainers:mysql:1.16.3")
Configuration : 2. Container 설정
생성할 컨테이너와 관련된 설정이다. Spring과 의존성을 위해 @TestConfiguration
을 이용하였다.
@TestConfiguration("TestMySQLContainer")
class TestMySQLContainer {
companion object {
@Container
@JvmStatic
val container = MySQLContainer<Nothing>("mysql:8.0.19")
.apply {
withDatabaseName("test")
withUsername("root")
withPassword("root")
}
.apply {
start()
}
}
}
Configuration : 3. Datasource 빈 작성하기
해당 컨테이너와 연결될 데이터소스를 직접 정의한다.
@TestConfiguration
class DataSourceConfig {
@Bean
@DependsOn("TestMySQLContainer")
fun dataSource(): HikariDataSource {
return DataSourceBuilder.create()
.type(HikariDataSource::class.java)
.url(TestMySQLContainer.container.jdbcUrl)
.username(TestMySQLContainer.container.username)
.password(TestMySQLContainer.container.password)
.build()
}
}
컨테이너가 생성된 이후 빈이 생성되도록 DependsOn
어노테이션을 이용하였다.
또한 MySQLContainer
타입으로 컨테이너를 생성했기 때문에 객체가 jdbcUrl
, username
등 데이터소스 생성에 필요한 변수들을 제공해준다.
특히 jdbcUrl
을 통해서 포트, dbname 등에 상관없이 데이터소스를 생성할 수 있다.
DataJpaTest 에서의 테스트
DataJpaTest
를 이용하면 테스트에 필요한 빈들만 띄워서 테스트할 수 있다.
@DataJpaTest
class FooTest {}
DataJpaTest
는 h2
데이터베이스를 테스트에 이용하는데, 먼저 이거를 disable 시켜줘야 한다.
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
또한 DataJpaTest
는 컴포넌트스캔을 하지 않으므로, 명시적으로 필요한 빈들을 Import 해야 한다. 위에서 생성한 두개의 클래스를 Import한다.
@DataJpaTest
@Import(TestMySQLContainer::class, DataSourceConfig::class)
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
이제 테스트를 실행한다면 기존의 AutoConfiguration 대신 직접 생성한 DataSource
가 적용되어 테스트가 실행된다.
Streaming Log(Container -> Spring Process)
마지막으로 MySQL 컨테이너 로그를 스프링 테스트 환경에서 볼 수 있도록 로그를 파이프 해줄 수 있다.
먼저 앞에서 생성한 TestMySQLContainer
클래스를 다음과 같이 수정한다.
@TestConfiguration("TestMySQLContainer")
class TestMySQLContainer {
companion object {
val logger = LoggerFactory.getLogger(TestMySQLContainer::class.java)
@Container
@JvmStatic
val container = MySQLContainer<Nothing>("mysql:8.0.19")
.apply {
withDatabaseName("test")
withUsername("root")
withPassword("root")
}
.apply {
start()
}
.apply {
followOutput(Slf4jLogConsumer(logger))
}
}
SLF4J
를 이용해 로그 인스턴스를 만들어 파이프해준다.
References
'공부 > BE, DB' 카테고리의 다른 글
Kryo Serializer (0) | 2022.12.27 |
---|---|
[Spring] Kotlin+JPA에서의 No-Args Constructor 이용 (0) | 2022.02.23 |
[Spring] QueryDSL Introduction ( w/ Spring Data JPA, Kotlin ) (0) | 2022.02.22 |
[Typeorm] 쿼리가 두번 나가는 이유? (0) | 2022.02.19 |
[Typeorm] Repository.save 메소드 알아보기 (0) | 2022.02.11 |