🐳

Spring Boot + KotestでTestcontainersを活用する方法:@ServiceConnectionを使ったテスト構築

2024/09/17に公開

以下の記事を読み、Spring Bootのv3.1.0から@ServiceConnectionが導入されたというのを知り、Kotestでも使えないかと試したので記事にします。

https://www.docker.com/blog/spring-boot-application-testing-and-development-with-testcontainers/

公式のTestcontainersの章ではJUnit 5用の@Testcontainersの使用例が多く、Kotestでは使えないかもと過去の自分と同じように感じている方の助けになれば幸いです。

https://docs.spring.io/spring-boot/reference/testing/testcontainers.html

@ServiceConnectionとは

@ServiceConnectionは、Spring Boot 3.1.0から導入されたアノテーションで、Testcontainersで起動したコンテナの接続情報を自動的にSpringのApplicationContextに登録できます。
これにより、テスト時に必要なサービス(データベースやメッセージブローカーなど)の接続情報を簡潔にセットアップできます。

従来、Spring BootでTestcontainersを使用する際にはコンテナのポートや接続URLを取得して、それを@DynamicPropertySourceを利用しApplicationContextに設定する必要がありました。

https://docs.spring.io/spring-framework/reference/testing/annotations/integration-spring/annotation-dynamicpropertysource.html

しかし、@ServiceConnectionを使用することで、これらの設定を自動化し、コードをよりシンプルに保つことができます。

KotestでのTestcontainersの利用

KotestのextensionにはTestcontainers用のサポートもあります。

https://kotest.io/docs/extensions/test_containers.html

ただし今回はSpring Bootとの連携を重視するため、この記事では@ServiceConnectionを使用し、TestcontainersとSpring Bootを統合する方法を紹介します。

セットアップ

Kotlinを使ったSpring Bootの開発環境のセットアップやKotestとSpring Bootのインテグレーションについては他の方が記事を書いてくださっているので省略します。

もし0から構築する場合は以下のSpring Initializrで必要な設定を追加し、Kotestの提供するSpring extensionを設定してください。

また今回の記事ではSpringExtensionをグローバルで設定している前提で記事を書いています。

src/test/kotlin/ProjectConfig.kt
import io.kotest.core.config.AbstractProjectConfig
import io.kotest.extensions.spring.SpringExtension

object ProjectConfig : AbstractProjectConfig() {
    override fun extensions() = listOf(SpringExtension)
}

作成したプロジェクトまたは既存のSpring Bootプロジェクトに、以下のように依存関係を追加します。

build.gradle.kts
dependencies {
    testImplementation("org.springframework.boot:spring-boot-testcontainers")
    testImplementation(platform("org.testcontainers:testcontainers-bom:{testcontainers_version}"))
    // 以下は自分の使うDBによって変える
    testImplementation("org.testcontainers:mysql")
    testImplementation("org.testcontainers:r2dbc")
    // Kotest用の依存関係
    testImplementation("io.kotest:kotest-runner-junit5:{kotest_version}")
    testImplementation("io.kotest.extensions:kotest-extensions-spring:{kotest_spring_extension_version}")
}

{testcontainers_version}{kotest_version}は、使用するバージョンに置き換えてください。

@ServiceConnectionを使用した設定

DatabaseTestConfiguration.kt

@ServiceConnectionを使用して、TestcontainersのコンテナをSpringのBeanとして定義します。
以下はMySQLコンテナを設定する例です。

DatabaseTestConfiguration.kt
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.boot.testcontainers.service.connection.ServiceConnection
import org.springframework.context.annotation.Bean
import org.testcontainers.containers.MySQLContainer
import org.testcontainers.utility.MountableFile.forHostPath

@TestConfiguration(proxyBeanMethods = false)
class DatabaseTestConfiguration {
    @Bean
    @ServiceConnection
    fun mySQLContainer(): MySQLContainer<Nothing> {
        val dockerImageName = System.getenv("MYSQL_DOCKER_IMAGE") ?: "mysql:8.0"
        val databaseName = System.getenv("DB_NAME") ?: "testdb"
        val sqlInitDir = "/docker-entrypoint-initdb.d/"

        return MySQLContainer<Nothing>(dockerImageName).apply {
            startupAttempts = 1
            withDatabaseName(databaseName)
            withCopyToContainer(forHostPath("path/to/schema.sql"), sqlInitDir + "000-schema.sql")
        }
    }
}

※環境変数が設定されていない場合はデフォルト値が使用されますが、必要に応じて.envファイルなどで環境変数を設定してください。

説明

  • @TestConfiguration:テスト用の追加のBeanを定義するためのアノテーション。
  • @Bean@ServiceConnection:このメソッドがTestcontainersのコンテナを返し、その接続情報をSpring Bootに提供する。
  • MySQLContainer:Testcontainersが提供するMySQL用のコンテナクラス。
  • withCopyToContainer:ローカルのSQLスクリプトをコンテナ内にコピーし、初期化時に実行する。

その他の詳しい設定については公式ドキュメントを参照してください。

https://java.testcontainers.org/

テストクラスでの使用

DatabaseTestConfigurationをテストクラスでインポートし、Kotestのテストを記述します。

HogehogeSpec.kt
import io.kotest.core.spec.style.DescribeSpec
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.context.annotation.Import

@SpringBootTest
@Import(DatabaseTestConfiguration::class)
class HogehogeSpec : DescribeSpec({
    // testの記述
})

説明

  • @SpringBootTest:Spring Bootのテストコンテキストをロードする。
  • @Import(DatabaseTestConfiguration::class):先ほどのデータベース設定をインポートする。

まとめ

Kotestでも@ServiceConnectionを活用して、TestcontainersとSpring Bootの統合テストをシンプルに実装できることを確認しました。

JUnit 5やKotest以外のテスティングフレームワークを使用している場合でも、Spring Bootの機能を利用しているため、他のフレームワークでも活用できます。

ぜひ、この方法を試してみて、テスト開発の効率化を図ってみてください。

Discussion