TestContainers를 이용한 테스트 환경 구성 공부/BE, DB

Date 2022. 3. 1. 17:25

Test 환경 구성 방법

테스트 환경을 구성하기 위해서는 다양한 방법이 존재한다.
대표적으로 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 {}

 

DataJpaTesth2 데이터베이스를 테스트에 이용하는데, 먼저 이거를 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

TestContainers

TestContainer로 멱등성 있는 테스트..

Recent Posts

Popular posts

Recent Comments

Tag List

인증 컨테이너 네트워크 DNS 도커 네임스페이스 AWS 백준 API 운영체제 IAC ORM 리눅스 JavaScript 파이썬 클라우드 DB 인가 알고리즘 TypeScript k8s GCP 테라폼 JWT
Total : Today : Yesterday :
Blog powered by Tistory, Designed by hanarotg