주요글: 도커 시작하기

요즘 MariaDB 바이너리 로그를 이용한 간단한 라이브러리를 만들고 있는데(mariadb-cdc 라이브러리) 다양한 버전의 MariaDB와 몇 가지 다른 설정으로 테스트할 필요가 있었다. 로컬에 설치한 MariaDB로는 다양한 테스트를 수행하는데 불편함이 있어 방법을 찾다가 Testcontainers가 있다는 것을 알게 되었다.

 

Testcontainers는 JUnit에서 테스트하는 동안 도커 컨테이너를 임시로 생성하고 제거해 주는 라이브러리다. Testcontainers를 사용하면 MariaDB, MongoDB, MySQL 등 다양한 DB에 대한 통합 테스트 환경을 쉽게 구성할 수 있다.

 

유튜브로 보기: youtu.be/eZbLAD2yUfE

의존 설정(JUnit5 기준)

JUnit에서 Testcontainers를 사용하려면 다음 의존 설정을 추가한다.

  • org.testcontainers:testcontainers:1.14.3
  • org.testcontainers:mariadb:1.14.3
  • org.testcontainers:junit-jupiter:1.14.3

그레이들 설정 예

dependencies {
    implementation("org.mariadb.jdbc:mariadb-java-client:2.7.0")
    implementation("ch.qos.logback:logback-classic:1.2.3")
    testImplementation(platform('org.junit:junit-bom:5.7.0'))
    testImplementation('org.junit.jupiter:junit-jupiter')
    testImplementation("org.testcontainers:testcontainers:1.14.3")
    testImplementation("org.testcontainers:mariadb:1.14.3")
    testImplementation("org.testcontainers:junit-jupiter:1.14.3")
}

test {
    useJUnitPlatform()
}

메이븐 설정 예

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.junit</groupId>
            <artifactId>junit-bom</artifactId>
            <version>5.7.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>org.mariadb.jdbc</groupId>
        <artifactId>mariadb-java-client</artifactId>
        <version>2.7.0</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.3</version>
    </dependency>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.testcontainers</groupId>
        <artifactId>testcontainers</artifactId>
        <version>1.14.3</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.testcontainers</groupId>
        <artifactId>mariadb</artifactId>
        <version>1.14.3</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.testcontainers</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>1.14.3</version>
        <scope>test</scope>
    </dependency>
</dependencies>

JUnit에서 MariaDB 컨테이너 구동하기

의존을 추가했다면 간단하게 MariaDB 컨테이너를 구동하고 연동할 수 있다. 다음 세 가지만 하면 된다.

  • @Testcontainers 애노테이션 테스트 클래스에 적용
  • MariaDBContainer 타입 필드를 선언하고 객체 생성
  • MariaDBContainer 필드에 @Container 애노테이션 적용

아래 코드는 작성 예이다.

import org.testcontainers.containers.MariaDBContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

@Testcontainers
public class MariadbTest {
    Logger logger = LoggerFactory.getLogger(getClass());

    @Container
    MariaDBContainer mariaDB = new MariaDBContainer();

    @Test
    void connect() {
        logger.info("host: {}", mariaDB.getHost());
        logger.info("port: {}", mariaDB.getMappedPort(3306));
        logger.info("username: {}", mariaDB.getUsername());
        logger.info("password: {}", mariaDB.getPassword());
        logger.info("jdbc url: {}", mariaDB.getJdbcUrl());
        try (Connection conn = DriverManager.getConnection(
                    mariaDB.getJdbcUrl(), 
                    mariaDB.getUsername(), 
                    mariaDB.getPassword())
        ) {
            logger.info("got connection");
            // 코드
        } catch (SQLException ex) {
            ex.printStackTrace();
        } catch (InterruptedException e) {
        }
    }
}

위 테스트 코드를 실행하면 테스트 메서드를 실행하기 전에 MariaDB 이미지를 이용해서 컨테이너를 시작한다. 컨테이너가 시작되면 MariaDBContainer가 제공하는 다양한 메서드를 이용해서 DB 연결에 필요한 정보를 구할 수 있다. 실제 위 테스트 코드를 실행하면 다음 로그 메시지가 출력된다(예제에서 사용하는 로그 포맷은 깃헙 코드를 참고한다).

23:13:30.742 [Test worker] INFO  docker[mariadb:10.3.6] - Creating container for image: mariadb:10.3.6
23:13:31.060 [Test worker] INFO  docker[mariadb:10.3.6] - Starting container with ID: 79fb2e93168247a2d9643256aba2f8fad39445be855d96e7fecb92d8fba9593e
23:13:31.656 [Test worker] INFO  docker[mariadb:10.3.6] - Container mariadb:10.3.6 is starting: 79fb2e93168247a2d9643256aba2f8fad39445be855d96e7fecb92d8fba9593e
23:13:31.692 [Test worker] INFO  docker[mariadb:10.3.6] - Waiting for database connection to become available at jdbc:mariadb://localhost:32769/test using query 'SELECT 1'
23:13:44.089 [Test worker] INFO  docker[mariadb:10.3.6] - Container is started (JDBC URL: jdbc:mariadb://localhost:32769/test)
23:13:44.090 [Test worker] INFO  docker[mariadb:10.3.6] - Container mariadb:10.3.6 started in PT17.215S
23:13:44.102 [Test worker] INFO  mariadb.MariadbTest - host: localhost
23:13:44.102 [Test worker] INFO  mariadb.MariadbTest - port: 32769
23:13:44.102 [Test worker] INFO  mariadb.MariadbTest - username: test
23:13:44.102 [Test worker] INFO  mariadb.MariadbTest - password: test
23:13:44.102 [Test worker] INFO  mariadb.MariadbTest - jdbc url: jdbc:mariadb://localhost:32769/test
23:13:44.120 [Test worker] INFO  mariadb.MariadbTest - got connection

출력한 결과를 보면 Testcontainers 1.14.3 기준으로 MariaDB 버전이 10.3.6이고 DB 사용자와 암호가 test인 것을 알 수 있다. JDBC URL을 보면 사용할 DB 이름도 test이다. 포트 번호는 32769인데 이는 컨테이너의 3306 포트에 매핑된 로컬 포트가 32769인 것을 의미한다. 실제 컨테이너가 구동될 때마다 이 포트는 바뀐다.

 

테스트 코드를 실행하는 중간에 docker ps 명령어를 실행하면 mariadb:10.3.6 이미지를 사용한 컨테이너가 생성된 것을 확인할 수 있다.

C:\work\any\testcontainer-sample>docker ps
CONTAINER ID        IMAGE                               COMMAND                  CREATED             STATUS              PORTS                     NAMES
c45c731d82ae        mariadb:10.3.6                      "docker-entrypoint.s…"   2 seconds ago       Up 1 second         0.0.0.0:32771->3306/tcp   modest_shannon
92d9b2fd807d        testcontainersofficial/ryuk:0.3.0   "/app"                   4 seconds ago       Up 2 seconds        0.0.0.0:32770->8080/tcp   testcontainers-ryuk-c2d44a10-9754-4133-830e-53c6147a1b22

@Container 애노테이션이 붙은 필드가 static이 아니면 각 테스트 메서드를 실행할 때마다 도커 컨테이너를 시작하고 종료한다. 테스트 클래스에 있는 테스트 메서드가 동일한 도커 컨테이너를 사용하게 하고 싶다면 @Container 필드를 static으로 선언하면 된다.

커스텀 설정

MariaDB 버전을 쉽게 바꿀 수도 있다. 생성자에 사용할 이미지만 입력하면 된다. 이 외에 DB 사용자와 암호를 포함해 몇 가지 설정을 쉽게 변경할 수 있다. 다음은 변경 예이다.

@Testcontainers
public class MariadbInitTest {
    
    @Container
    JdbcDatabaseContainer mariaDB = new MariaDBContainer("mariadb:10.5") // 이미지
            .withConfigurationOverride("conf.d.105") // DB 서버 추가 설정
            .withUsername("myuser") // DB 사용자
            .withPassword("mypassword") // 암호
            .withDatabaseName("mydb") // 사용할 데이터베이스
            .withInitScript("init.sql"); // 초기 실행 쿼리

withConfigurationOverride()가 아닌 다른 메서드는 (MariaDBContainer 클래스의 상위 클래스인) JdbcDatabaseContainer 클래스에 정의되어 있다. 그래서 필드 타입을 JdbcDatabaseContainer 클래스로 선언했다.

 

withInitScript()는 클래스패스에서 실행할 쿼리를 찾는다. 예제 코드는 src/test/resources 폴더에 위치한 init.sql 파일을 실행한다.

 

withConfigurationOverride() 메서드는 MariaDB 설정 파일을 변경할 때 사용한다. 이 메서드는 클래스패스에 위치한 폴더명을 인자로 받는다. MariaDBContainer는 이 폴더의 파일을 컨테이너의 "/etc/mysql/conf.d" 폴더에 복사한다(withConfigurationOverride() 메서드를 설정하지 않으면 Testcontainers MariaDB 모듈이 제공하는 my.cnf 파일을 사용한다).

참고 자료

+ Recent posts