본문 바로가기
test & refactoring

[테스트 및 리팩토링 7편] TestContainer Example

by 무대포 개발자 2023. 4. 22.
728x90
반응형

source 는 Github 에 있습니다.

목차는 테스트 & 리팩토링 목차 에 있습니다.

[테스트&리팩토링 7편] TestContainer Example

TestContainer 란?

  • DB, 큐, 메시지 브로커 등을 제공해주는 가상 컨테이너입니다. Java, Kotlin 과 같은 JVM 기반에서 동작하며, Docker 를 필요로 합니다.
  • docker-compose 는 언어와 관계 없이 사용할 수 있으며, TestContainer 는 코드 레벨에서 제어할 수 있습니다.
  • 코드에서 제어할 수 있기에 docker 만 떠있다면 테스트 코드에 container 기동, 중단 로직을 넣어서 테스트를 수행할 때, 한 번에 수행할 수 있는 특징이 있습니다.

소스 설명

  • Spring boot, spring data jpa, MySQL 환경에서 DB 컨테이너를 띄워서 DB CRUD 를 진행하는 예제를 작성했습니다.

  • DB 만 테스트 하기 위해 @DataJpaTest 를 사용했습니다.

    소스

  • Test Source 와 Container, DataSource Config 를 작성했습니다. DataSource 를 빈초기화 할 때는, TestMySQLContainer 작업이 끝나야 MySQL DB Container 가 생성되기에 DependsOn 을 설정했습니다.

  • 나머지 소스는 github 에 있습니다.

// UserRepositoryTest.java
@DataJpaTest
@Import(TestMySQLContainer::class, DataSourceConfig::class)
class UserRepositoryTest {
// lateinit 이 변수가 나중에 초기화될 것이며, 선언할 때 즉시 초기화할 필요가 없다는 것을 의미
@Autowired
private lateinit var userRepository: UserRepository
@Test
fun `given User when findById then return User`() {
// given
val user = User(name = "HelloWorld...")
userRepository.save(user)
// when
val result = userRepository.findById(user.id!!)
// then
assertEquals(user, result.get())
}
}
// DataSourceConfig.java
@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()
}
}
// TestMySQLContainer.java
@TestConfiguration("TestMySQLContainer")
class TestMySQLContainer {
companion object {
val logger = LoggerFactory.getLogger(TestMySQLContainer::class.java)
@Container
@JvmStatic
val container = MySQLContainer<Nothing>("mysql:8.0.23")
.apply {
withDatabaseName("test")
withUsername("root")
withPassword("1234")
}
.apply {
start()
}
.apply {
followOutput(Slf4jLogConsumer(logger))
}
}
}

SharedContainer

  • Container annotation 을 사용하면 테스트 클래스가 수행될 때마다 새로운 인스턴스가 생성되므로, 테스트 간에 영향을 주지 않습니다. Shared Container 는 생성한 인스턴스를 재사용하는 것이기에 각각의 테스트에서는 인스턴스를 공유해서 사용할 수 있으며, 새로 생성할 필요가 없기에 부하가 줄게 됩니다.
  • 상황에 맞게 Container 를 쓸지 SharedContainer 를 쓸지 판단해서 쓰면 좋을 것 같습니다.

댓글