Spring Boot + KotestでTestcontainersを活用する方法:@ServiceConnectionを使ったテスト構築
以下の記事を読み、Spring Bootのv3.1.0から@ServiceConnection
が導入されたというのを知り、Kotestでも使えないかと試したので記事にします。
公式のTestcontainersの章ではJUnit 5用の@Testcontainers
の使用例が多く、Kotestでは使えないかもと過去の自分と同じように感じている方の助けになれば幸いです。
@ServiceConnectionとは
@ServiceConnection
は、Spring Boot 3.1.0から導入されたアノテーションで、Testcontainersで起動したコンテナの接続情報を自動的にSpringのApplicationContext
に登録できます。
これにより、テスト時に必要なサービス(データベースやメッセージブローカーなど)の接続情報を簡潔にセットアップできます。
従来、Spring BootでTestcontainersを使用する際にはコンテナのポートや接続URLを取得して、それを@DynamicPropertySource
を利用しApplicationContext
に設定する必要がありました。
しかし、@ServiceConnection
を使用することで、これらの設定を自動化し、コードをよりシンプルに保つことができます。
KotestでのTestcontainersの利用
KotestのextensionにはTestcontainers用のサポートもあります。
ただし今回はSpring Bootとの連携を重視するため、この記事では@ServiceConnection
を使用し、TestcontainersとSpring Bootを統合する方法を紹介します。
セットアップ
Kotlinを使ったSpring Bootの開発環境のセットアップやKotestとSpring Bootのインテグレーションについては他の方が記事を書いてくださっているので省略します。
もし0から構築する場合は以下のSpring Initializrで必要な設定を追加し、Kotestの提供するSpring extensionを設定してください。
また今回の記事ではSpringExtension
をグローバルで設定している前提で記事を書いています。
import io.kotest.core.config.AbstractProjectConfig
import io.kotest.extensions.spring.SpringExtension
object ProjectConfig : AbstractProjectConfig() {
override fun extensions() = listOf(SpringExtension)
}
作成したプロジェクトまたは既存のSpring Bootプロジェクトに、以下のように依存関係を追加します。
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コンテナを設定する例です。
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スクリプトをコンテナ内にコピーし、初期化時に実行する。
その他の詳しい設定については公式ドキュメントを参照してください。
テストクラスでの使用
DatabaseTestConfiguration
をテストクラスでインポートし、Kotestのテストを記述します。
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