Spring Boot における結合テスト環境のカイゼン〜H2 から Testcontainers への移行〜
はじめに
テスト用データベースに H2 を使うのをやめて、Testcontainers で立てた DB コンテナを使うように変更したので、背景や Testcontainers の導入方法などについてご紹介します。
環境
- Java 17
- Spring Boot 3.3.5
- spring-boot-testcontainers 3.3.5
なぜ Testcontainers に移行したか?
以前は以下のような構成で、ローカルとテストで異なる DB で開発をしていました。
- ローカル
- 本番環境と同じバージョンの MariaDB を Docker で立てて開発
- テスト
- H2 を使用
- Spring の
@Sql
アノテーション を使ってテストデータを投入
H2 はインメモリデータベースとして導入が簡単で、これまで便利に使っていましたが、大きく2つの問題がありました。
- テストの信頼性の問題
- 本番環境と異なるデータベースのため、テストが実際の動作を正確に反映しない
- 互換性の問題
- H2 はある程度他のデータベースとの互換性を持つものの、完全ではありません。[1]
上記の内、移行のきっかけになったのは互換性の問題です。
ある機能の結合テストを書く際に、ローカル環境での動作確認用に使用していた SQL をそのまま @Sql
で投入しようとしたところ、互換性の問題でエラーになる事案が発生しました。
以前から、テストの信頼性の問題は認識していましたが、何か問題が顕在化している訳でもなかったので、どこかで Testcontainers を導入しようかなぐらいに思ってたところ、互換性の問題が顕在化したので、これを機に Testcontainers に移行しました。
Testcontainers の導入
このあたりを参考に実装しました。
ざっくりと、やったことやポイントを解説します。
まず、必要なライブラリを追加しました。
dependencies {
testImplementation 'org.springframework.boot:spring-boot-testcontainers'
testImplementation 'org.testcontainers:mariadb'
testImplementation 'org.mariadb.jdbc:mariadb-java-client'
}
次に、以下のようにしてコンテナを構成しました。[2]
public abstract class AbstractIntegrationTest { // ①
static final MariaDBContainer<?> mariadb;
@ServiceConnection // ②
static {
mariadb =
new MariaDBContainer<>(DockerImageName.parse("mariadb:10.6.14"))
.withDatabaseName("db")
.withCopyFileToContainer(MountableFile.forClasspathResource("schema.sql"), "/docker-entrypoint-initdb.d/01-schema.sql")
.withEnv("TZ", "Asia/Tokyo");
mariadb.start();
}
}
① では、AbstractIntegrationTest
という結合テスト用の基底クラスを作成してコンテナを起動させています。
各結合テストは、AbstractIntegrationTest
を継承して実装します。
このように書くことで、コンテナがシングルトンになり、一度起動したコンテナを使い回せるので、テストを高速に実行できます。
もし、テストごとにコンテナを起動する場合は、@Testcontainers
と @Container
を使います。[3]
②では、@ServiceConnection
アノテーションを使って、コンテナ立ち上げ時に動的に決まる DB の接続情報を spring.datasource.url
などのプロパティに設定しています。
Spring Boot 3.0 までは @DynamicPropertySource
を使って実装する必要がありましたが、3.1 から @ServiceConnection
アノテーションを付与するだけで済むようになりました。[4]
その他、ポートは動的に決めるべきとか、ファイルの配置はマウントよりもコピーの方がいいなどのプラクティスも取り込みました。
おわりに
Testcontainers を使った Spring Boot における結合テスト環境のカイゼンについてご紹介しました。
導入する中で、「既存の SQL に H2 だと動くけど、MariaDB だと動かない」ものがあることに気づけたり、導入後、DB 以外のコンテナも Testcontainers で用意する動きが広がって自動テストで担保できる範囲が増えたので、導入してよかったと思います!
-
モードによって特定の DB の機能の一部をエミュレートできます。
https://www.h2database.com/html/features.html ↩︎ -
実行には環境変数が必要な場合があります。
https://java.testcontainers.org/supported_docker_environment/ ↩︎ -
https://java.testcontainers.org/test_framework_integration/junit_5/ ↩︎
-
https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.1-Release-Notes#testcontainers ↩︎
私たち BABY JOB は、子育てを取り巻く社会のあり方を変え、「すべての人が子育てを楽しいと思える社会」の実現を目指すスタートアップ企業です。圧倒的なぬくもりと当事者意識をもって、こどもと向き合う時間、そして心のゆとりが生まれるサービスを創出します。baby-job.co.jp/
Discussion