スポンサーリンク

Spring Bootの@ServiceConnectionでテスト用DB接続を自動化する方法

スポンサーリンク
この記事は約28分で読めます。
スポンサーリンク

テストでPostgreSQL/MySQL/RedisをDockerで立ち上げたいのに、毎回 @DynamicPropertySource でURLやポートを書き足して…「またこの儀式か〜」ってなりませんか?
しかも、ちょっと依存が足りないだけで「接続先が入ってない!」って落ちたり、Docker ComposeとTestcontainersの話が頭の中で混ざって迷子になったり。あるあるです。

この記事では、Spring Boot 3.1+で使える @ServiceConnection を使って、そのしんどさを一気に減らします。
やることはシンプルで、

「このコンテナにSpringをつなげてね」って合図を出す

だけ。するとSpring Bootが、接続に必要な情報(URL/ホスト/ポート/ユーザー/パスワードなど)をテストに自動で注入してくれます。

先に最短の結論だけ言うと、起動するのはTestcontainers、つなぐのは @ServiceConnection です。
まずは「いちばん小さい成功例」で“動いた!”を作って、そのあとに「付ける場所」「必須依存」「Composeとの違い」「よくあるエラーの直し方」まで順番に片づけていきます。

今日から最小設定でテスト用DB接続、ラクにしちゃいましょう。

  1. @ServiceConnectionを一言で言うと?何を自動でやる?
    1. まず結論:「このコンテナにSpringをつなげてね」の合図
    2. 何が省ける?(URL/ユーザー/パス/ホスト/ポートの手書き)
    3. どこまで自動?(コンテナ起動 vs 接続情報の注入)
  2. いちばん小さい成功例(PostgreSQL/Redis)を1画面で
    1. Java(JDBC)最小例:@Testcontainers + @Container + @ServiceConnection
    2. Kotlin最小例:companion object と @JvmFieldの注意
    3. Redis例:アプリ側設定ゼロで疎通する感覚をつかむ
  3. どこに付けるの?2つの流派を図で整理(A:staticフィールド / B:@Bean)
    1. A案:テストクラスの static @Container(いちばん楽)
    2. B案:@TestConfigurationで@Bean(複数テストで使い回す)
    3. どっちを選ぶ?(判断基準:使い回し/拡張/見通し)
  4. 必須チェックリスト(ここが抜けると接続できない)
    1. Spring Bootのバージョンと前提(JUnit5 / Docker起動)
    2. 依存関係:何をtestに足す?(Boot側/TC側)
    3. Springが「見つけられる形」になってる?(static or Bean)
  5. Docker Compose連携とTestcontainers連携の違い(ごちゃごちゃ解消)
    1. 目的が違う:開発環境を立てる vs テストで完結させる
    2. Composeのとき@ServiceConnectionは必要?(必要な場面/不要な場面)
    3. 迷ったらこの早見ルール(開発・CI・ローカル)
  6. よくあるエラー別:原因→直し方(最短で復旧する)
    1. 「接続先が入ってない」:依存不足 / アノテーション位置ミス
    2. 「ポートが違う」:固定ポート思考を捨てて“割り当て後ポート”へ
    3. 「コンテナは起動したのに接続できない」:起動待ち/ネットワーク/権限
    4. CIで落ちる:Docker無し/リソース不足/並列実行
  7. 複数接続が勝手に作られて混乱する問題(JDBCとR2DBCなど)
    1. 何が起きてる?(自動設定が“両方作ろう”とするケース)
    2. type属性で「作る接続情報をしぼる」例
    3. それでも迷う時の整理(どのStarterを入れてる?)
  8. 対応サービス/対応外の逃げ道(自作コンテナでも詰まない)
    1. よく使う対応例(DB/Redis/RabbitMQなど)と選び方
    2. 対応外なら保険:@DynamicPropertySource / DynamicPropertyRegistrar
    3. さらに踏み込む:独自対応(拡張)への導線(考え方だけ)
  9. 最後に:どれを選ぶ?早見表 + コピペ用テンプレ(CTA)
    1. 選び方早見表(Compose / Testcontainers / 動的プロパティ)
    2. コピペ置き場(Java/Kotlin・JDBC/R2DBCの最小テンプレ)
    3. 次の一歩(自分のプロジェクトに入れる手順)
  10. よくある質問
スポンサーリンク

@ServiceConnectionを一言で言うと?何を自動でやる?

まず結論:「このコンテナにSpringをつなげてね」の合図

@ServiceConnection は超ざっくり言うと、「このコンテナにSpringをつなげてください」っていう合図です。
テストでコンテナ(PostgreSQLやRedisなど)を立てたあと、Spring Bootが「じゃあ接続先メモを作って、必要な場所に入れておきますね!」とやってくれます。

覚えるフレーズはこれでOKです👇

合図(@ServiceConnection)=橋渡し=接続先メモを自動注入

何が省ける?(URL/ユーザー/パス/ホスト/ポートの手書き)

今まで @DynamicPropertySource で手書きしてた、こういうやつが消えます👇

  • DBの URL(どのホスト?どのポート?まで込み)
  • ユーザー名/パスワード
  • Redisの host/port

つまり「コンテナの情報を読んで、Springの設定に書き込む作業」を、Spring Bootが肩代わりしてくれるイメージです。

どこまで自動?(コンテナ起動 vs 接続情報の注入)

ここが混ざりやすいので、分けて覚えるのがコツです。

  • コンテナを起動するのはTestcontainers(いつも通り @Container などで立てる)
  • 接続情報をSpringに入れるのが@ServiceConnection(URLやポートを“あとから決まる値”として自動で渡す)

なので、@ServiceConnection は「勝手に全部やってくれる魔法」ではなく、起動したコンテナとSpringの間をつなぐ橋渡しだと思ってください。ここが分かると、一気に迷子が減ります。

いちばん小さい成功例(PostgreSQL/Redis)を1画面で

理屈はあとです。まずは「え、URL書いてないのに動いた…!」を作ります。

ポイントはこれだけです👇

コンテナ(Testcontainers)に @ServiceConnection を付ける → Springが接続先メモを自動で入れる

Java(JDBC)最小例:@Testcontainers + @Container + @ServiceConnection

@SpringBootTest
@Testcontainers
class PostgresIT {

  @Container
  @ServiceConnection // ←「このコンテナにSpringをつなげてね」の合図
  static PostgreSQLContainer<?> postgres =
      new PostgreSQLContainer<>("postgres:16-alpine");

  @Autowired JdbcTemplate jdbc;

  @Test
  void it_works() {
    Integer one = jdbc.queryForObject("select 1", Integer.class);
    assertThat(one).isEqualTo(1);
  }
}

ここがポイント(3つだけ)

  • spring.datasource.url とか 一切書かないのが正解です(書くと逆に邪魔になりがちです)
  • 起動は @Container、接続情報の注入は @ServiceConnection の担当です
  • テストで JdbcTemplate が普通に動いたら勝ちです

Kotlin最小例:companion object と @JvmFieldの注意

Kotlinはここでコケやすいです。“staticフィールド扱い”にするのが大事で、そこで @JvmField が登場します。

@SpringBootTest
@Testcontainers
class PostgresIT {

  companion object {
    @Container
    @ServiceConnection
    @JvmField // ←これがないと「static扱い」にならず、見つけてもらえないことがあります
    val postgres = PostgreSQLContainer("postgres:16-alpine")
  }

  @Autowired lateinit var jdbc: JdbcTemplate

  @Test
  fun itWorks() {
    val one = jdbc.queryForObject("select 1", Int::class.java)
    assertThat(one).isEqualTo(1)
  }
}

ここがポイント(3つだけ)

  • companion object の中でも @JvmField を付けてstatic化するのが安全です
  • KotlinでもやることはJavaと同じ:起動はTC、つなぐのは@ServiceConnection
  • URL/ポートをどこにも書いてないのに繋がる、が成功です

Redis例:アプリ側設定ゼロで疎通する感覚をつかむ

Redisも「host/portどこだっけ…」が消えます。コンテナだけ置いて、あとはSpringに任せます。

@SpringBootTest
@Testcontainers
class RedisIT {

  @Container
  @ServiceConnection // ←redis:~ のコンテナなら、接続先メモを自動で入れてくれます
  static GenericContainer<?> redis =
      new GenericContainer<>("redis:7-alpine").withExposedPorts(6379);

  @Autowired StringRedisTemplate redisTemplate;

  @Test
  void it_works() {
    redisTemplate.opsForValue().set("hello", "world");
    assertThat(redisTemplate.opsForValue().get("hello")).isEqualTo("world");
  }
}

ここがポイント(3つだけ)

  • spring.redis.host / spring.redis.port書かないのが気持ちいいところです
  • Redisは 割り当てポートが毎回変わってもOK(固定ポート期待を捨てるのがコツです)
  • set/get が通れば「自動で入った感」つかめます
筆者
筆者

次は「これ、どこに付けるのが正解?」問題を、2つの流派でスパッと整理します。

どこに付けるの?2つの流派を図で整理(A:staticフィールド / B:@Bean)

@ServiceConnection って、結局どこに付けるんですか?」で止まる人、多いです。
結論から言うと、Spring Bootが見つけられる形は 2つ だけです👇

  • A:テストクラスにある static なコンテナ(いちばん楽)
  • B:SpringのBeanとして登録されたコンテナ(使い回し向き)

ポイントは「Springが見つけられる形」です。
“そのコンテナ、Springから見えてます?” ここだけ外すと、接続先メモが注入されません。

A案:テストクラスの static @Container(いちばん楽)

これが最短ルートです。テストクラス内に static(Kotlinなら companion object + @JvmField)で置きます。

@SpringBootTest
@Testcontainers
class PostgresIT {

  @Container
  @ServiceConnection
  static PostgreSQLContainer<?> postgres =
      new PostgreSQLContainer<>("postgres:16-alpine");

  // ... tests
}

A案のいいところ

  • 1ファイルで完結、脳みそがラクです
  • 「とりあえず動かす」が最速です
  • そのテストだけで使うならこれで十分です

A案の注意

  • 複数テストで同じ定義をコピペし始めると、だんだんダルくなります

B案:@TestConfigurationで@Bean(複数テストで使い回す)

「いろんなテストで同じDBコンテナ使いたい」「設定を1か所にまとめたい」ならこっちです。
SpringのBeanとしてコンテナを登録して、そこに @ServiceConnection を付けます。

@SpringBootTest
class PostgresIT {

  @TestConfiguration(proxyBeanMethods = false)
  static class ContainerConfig {

    @Bean
    @ServiceConnection
    PostgreSQLContainer<?> postgres() {
      return new PostgreSQLContainer<>("postgres:16-alpine");
    }
  }

  // ... tests
}

B案のいいところ

  • 複数テストで使い回しやすい(共通化しやすい)
  • 「DBだけ差し替えたい」「オプション足したい」がやりやすい
  • テスト構成が大きくなっても整理しやすい

B案の注意

  • “Springの設定っぽさ”が増えるので、最初はA案よりちょい難しく感じます

どっちを選ぶ?(判断基準:使い回し/拡張/見通し)

迷ったら、このルールでOKです👇

  • 単発テスト派 / まず動かしたい派 → A案(static @Container)
  • 共通化派 / 複数テストで使う派 → B案(@Bean)
  • 将来オプション増えそう(ネットワーク、初期データ投入、複数コンテナ連携) → B案が強い

そして共通の大事ポイントをもう一回だけ言います。
@ServiceConnection“Springが見つけられる形(static or Bean)” に置かないと効きません。
ここさえ守れば、「接続情報の手書き」はかなり消せます。

筆者
筆者

次は、ここまでやっても「なぜか接続できない…」を防ぐための 必須チェックリスト に行きます。

必須チェックリスト(ここが抜けると接続できない)

「コードは合ってそうなのに、なぜか接続できない…」は、だいたい 前提 or 依存 or 置き場所 のどれかが抜けてます。
ここは3分で終わる YES/NOチェックで潰しちゃいましょう。

Spring Bootのバージョンと前提(JUnit5 / Docker起動)

  • Spring Boot 3.1+ ですか?(3.2でも4.xでもOKです)
    • 3.0以前だと、そもそも @ServiceConnection の世界じゃないです。
  • JUnit 5 を使ってますか?(junit-jupiter 系)
  • Dockerが起動してますか?
    • Docker Desktop / Colima / Podman など、どれでもいいです
    • ただし「Docker自体が動いてない」と、当然コンテナは立ちません

依存関係:何をtestに足す?(Boot側/TC側)

ここ、箱で分けると一気に分かりやすいです👇

① Boot側(Springが“自動注入”するためのやつ)

  • spring-boot-testcontainers(テスト連携のカギ)
  • 使うもののstarter(例:JDBCなら spring-boot-starter-jdbc、Redisなら spring-boot-starter-data-redis など)

② Testcontainers側(コンテナを立てるやつ)

  • testcontainers 本体
  • JUnit連携:junit-jupiter
  • DBモジュール:postgresql / mysql など(DBによって追加)

ざっくり言うと、起動するための依存(TC)つなぐための依存(Boot) が両方必要です。片方だけだとハマります。

※この章は「何を足す?」が目的なので、ビルドツールは好みでOKです(Maven/Gradleどっちでも動きます)。

Springが「見つけられる形」になってる?(static or Bean)

最後のチェックがこれです。見落とし率高めです。

  • コンテナ定義は A:staticフィールド になってますか?
    • Java:static@Container
    • Kotlin:companion object@JvmField(これ忘れがちです)
  • もしくは B:@Bean でSpring管理になってますか?(@TestConfiguration など)
  • @ServiceConnectionそのコンテナ(または@Bean)に付いてますか?
    • テストクラスに付けてもダメです。「合図」はコンテナ本人に付けます。

このチェックで引っかかるところを直すだけで、「接続先が入ってない」「なんかnullっぽい」系の事故はかなり減ります。

筆者
筆者

次は、頭がごちゃつきやすい Docker Compose連携とTestcontainers連携の違い をスッキリ分けます。

Docker Compose連携とTestcontainers連携の違い(ごちゃごちゃ解消)

目的が違う:開発環境を立てる vs テストで完結させる

まずここで混ざりがちなので、目的で分けるのが一番ラクです。

  • Docker Compose:手元の開発環境を立てる用
    → 「アプリを起動する前に、DBやRedisを先に立てておく」イメージです。
  • Testcontainers:テストの中で完結させる用
    → 「テストが始まったら立ち上げて、終わったら片付ける」イメージです。

なので、基本の合言葉はこれです👇

開発=Compose / テスト=Testcontainers

Composeのとき@ServiceConnectionは必要?(必要な場面/不要な場面)

ここ、結論から言うと “ケースによる” です。ポイントは、Springが「接続先メモ」を作る材料を持っているかどうかです。

✅ 不要になりやすい場面(多くの人はここ)

  • Composeを「外で」起動している(docker compose up してからアプリ起動)
    • Springから見ると、ただの“外部のDB”です。なので いつも通り spring.datasource.url などは必要になりやすいです。

※この場合、@ServiceConnection を付ける場所(コンテナの実体)がSpring側に存在しないので、合図の出しようがないです。

✅ 必要になる場面(テストに寄せたいとき)

  • テストのコード側でコンテナを「管理している」とき
    • たとえば Testcontainersでコンテナを握っている(static/@Beanで置ける)なら、@ServiceConnection が効きます。

「Composeも使うけど、テストはテストで自己完結させたい」なら、基本はこっちの考え方です。

ざっくりまとめると👇

  • “外で立ってるCompose”@ServiceConnection の出番は薄い(Springがコンテナを見れない)
  • “テスト側が立てるコンテナ”@ServiceConnection が刺さる(Springがコンテナを見れる)

迷ったらこの早見ルール(開発・CI・ローカル)

迷ったら、いったんこのルールで決めちゃってOKです。

シーンおすすめ理由
ローカル開発(手元で動かす)Composeいつでも同じ環境をサッと立てたいからです
ローカルテスト(./gradlew test / mvn testTestcontainers + @ServiceConnectionテストが勝手に立てて勝手に片付けるのがラクだからです
CI(GitHub Actionsなど)まずはTestcontainers“テストが自己完結”が一番事故りにくいです(外部依存を減らせます)

要するに、Composeは「開発の再現」Testcontainersは「テストの完結」

この線引きができると、「Composeの設定とテストの設定が二重になってしんどい」みたいな混線がかなり減ります。

筆者
筆者

次は、いよいよ困ったときに一番助かる “よくあるエラー別:原因→直し方” に行きます。

よくあるエラー別:原因→直し方(最短で復旧する)

ここは「ログ全文は読めない!」でも復旧できるように、症状の見た目で分けます。

型はいつもこれです👇

  • 症状
  • 原因トップ3
  • 確認
  • 直し方

「接続先が入ってない」:依存不足 / アノテーション位置ミス

症状あるある

  • spring.datasource.url is not set っぽい
  • Redisが localhost:6379 に行こうとしてコケる(勝手にデフォルトへ)

原因トップ3

  • spring-boot-testcontainers など Boot側の連携依存が足りない
  • @ServiceConnection付ける場所が違う(テストクラスに付けてる等)
  • コンテナが Springから見える形になってない(staticじゃない / @Beanじゃない)

確認

  • コンテナ定義は static(Kotlinは companion object + @JvmField) ですか? それとも @Bean ですか?
  • @ServiceConnectionコンテナ(または@Beanメソッド)本人に付いてますか?

直し方

  • 依存を見直す(Boot側:spring-boot-testcontainers / TC側:junit-jupiter + 各モジュール)
  • 置き場所をA案(static)かB案(@Bean)に寄せる
    → 「Springが見つけられる形」になった瞬間に直ることが多いです

「ポートが違う」:固定ポート思考を捨てて“割り当て後ポート”へ

これ、ほぼ全員が一回やります。

コンテナの5432/6379はコンテナ内部の番号で、PC側は別番号に割り当てられます。

イメージ👇(固定ポート期待=×)

× localhost:5432 に行く(固定)
○ 割り当て後ポートに行く(自動注入 or mapped port)

症状あるある

  • Connection refused: localhost:5432(Redisなら6379)
  • 「コンテナは起動してるのに繋がらない」

原因トップ3

  • localhost:5432決め打ちしてる
  • Composeの設定と混ざって、手書き設定が優先されてる
  • @ServiceConnection が効いてなくて、デフォルト接続先に行ってる

確認

  • テストや設定に localhost:5432 / localhost:6379 が残ってませんか?
  • @ServiceConnection を使うなら、URLやhost/portを基本書かない方向になってますか?

直し方

  • 決め打ちを削除して、@ServiceConnection に任せる(おすすめ)
  • どうしても自前で参照するなら、割り当て後を取ります
    • 例:Redisなら container.getMappedPort(6379) みたいなやつです(固定じゃない前提で考える)

「コンテナは起動したのに接続できない」:起動待ち/ネットワーク/権限

症状あるある

  • 起動ログは出てるのに、最初の接続だけ失敗する
  • DBはいるのに「認証NG」「タイムアウト」

原因トップ3

  • 起動待ち不足(DBが“起動中”なだけ)
  • ユーザー/パスワードなど初期値のズレ
  • コンテナの ネットワーク周り(別サービス連携時に多い)

確認

  • コンテナログを見て「ready」っぽい行が出てますか?
  • 認証エラーなら、コンテナの環境変数や初期値を疑うのが早いです

直し方

  • まずはログを見る(最短の近道です)
    • Docker側ログ / TestcontainersログをONにする
  • 起動が重いサービスは待ち時間を増やす(タイムアウト系はこれで直ること多いです)
  • 複数コンテナ連携なら「同じネットワークにいる?」を確認する

CIで落ちる:Docker無し/リソース不足/並列実行

症状あるある

  • ローカルはOK、CIだけNG
  • 「Dockerが見つからない」系、または妙なタイムアウト

原因トップ3

  • CIに Dockerがない/使えない
  • メモリ不足など リソース不足で起動が遅い
  • テスト並列でコンテナが増えて 重くなる

確認

  • CI環境でDockerが使える設定になってますか?
  • 失敗がタイムアウトなら、まずリソース不足を疑うと当たりやすいです

直し方

  • Dockerが使える実行環境にする(CIの設定でここが最重要です)
  • 並列を落とす/テストを軽くする/タイムアウトを伸ばす、のどれかで安定しやすいです
筆者
筆者

次は、ちょっと厄介な「勝手に接続が2つ作られて混乱する(JDBCとR2DBCなど)」問題を、スッキリ整理して制御します。

複数接続が勝手に作られて混乱する問題(JDBCとR2DBCなど)

何が起きてる?(自動設定が“両方作ろう”とするケース)

「え、同じPostgreSQLコンテナなのに、DataSource(JDBC)ConnectionFactory(R2DBC)も作ろうとしてません?」みたいな現象です。

だいたい原因はシンプルで、Starterを両方入れてるだけです(例:spring-boot-starter-jdbcspring-boot-starter-data-r2dbc を同居)。

さらにSpring Boot側は、1つのコンテナから JDBC用/ R2DBC用の接続先メモ(ConnectionDetails)を作れるので、両方候補が揃ってしまうんですね。

type属性で「作る接続情報をしぼる」例

ここで効くのが @ServiceConnectiontype属性です。「今日はJDBCだけでいいです!」って絞れます。

JDBCだけにしたい(Java)

@Container
@ServiceConnection(type = JdbcConnectionDetails.class)
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16-alpine");

JDBCだけにしたい(Kotlin)

@Container
@ServiceConnection(type = [JdbcConnectionDetails::class])
@JvmField
val postgres = PostgreSQLContainer("postgres:16-alpine")

逆にR2DBCだけなら R2dbcConnectionDetails を指定する、って感じです。

それでも迷う時の整理(どのStarterを入れてる?)

迷ったら、まずこれだけ確認してください👇

  • JDBCのStarter(spring-boot-starter-jdbc / spring-boot-starter-data-jpa)入ってる?
  • R2DBCのStarter(spring-boot-starter-data-r2dbc)入ってる?
  • どっちも要らないのに入ってない?(“試した名残”が一番多いです)

結論:Starterを減らすか、typeで絞る。この2択でほぼ片付きます。

対応サービス/対応外の逃げ道(自作コンテナでも詰まない)

よく使う対応例(DB/Redis/RabbitMQなど)と選び方

まず安心ポイントです。
@ServiceConnection は「よくあるやつ」にはちゃんと対応してます。

Spring Boot側(spring-boot-testcontainers)には、コンテナの種類に応じて接続先メモ(ConnectionDetails)を作る仕組みが用意されています。

よく見るところだとこんな感じです👇

  • JDBC系DBJdbcDatabaseContainer に対して JdbcConnectionDetails(PostgreSQL/MySQLなどの“JDBCコンテナ”がここに乗ります)
  • Redis / RabbitMQ / Kafka / MongoDB / Elasticsearch / Cassandra / Couchbase / ActiveMQ / Artemis なども、対応する接続先メモが用意されています

選び方はシンプルで、迷ったらこの順です。

  • Testcontainersに「専用Containerクラス」がある?(あるならそれを使う)
  • GenericContainer しかない? → イメージ名で推測できるか、ダメなら次の保険へ

対応外なら保険:@DynamicPropertySource / DynamicPropertyRegistrar

「対応外っぽい…」は負けじゃないです。保険ルートがちゃんとあります。

  • まずは定番の @DynamicPropertySource
    → “必要なプロパティだけ”をテスト中に差し込めます。Spring Bootのリファレンスでも、Service Connectionの代替として紹介されています。
  • もう1つの保険が DynamicPropertyRegistrar
    @DynamicPropertySource が書きづらいケースでも、Beanとして登録できる逃げ道です。

要するに、対応してたら@ServiceConnection、対応してなさそうなら動的プロパティでOKです。

さらに踏み込む:独自対応(拡張)への導線(考え方だけ)

「自作コンテナでも@ServiceConnectionしたい!」ってときは、考え方はこうです👇

  • Spring Bootは @ServiceConnection から “接続先メモ(ConnectionDetails)” を作って
  • 各オート設定がそれを読んで、つながるようにします

なので上級者向けには、「自分のコンテナに合うConnectionDetailsを用意して、Springに見つけてもらう」みたいな拡張もできます(深追いは別記事級です)。

筆者
筆者

次はラスト!「結局どれ選ぶ?」を早見表+コピペテンプレで迷いゼロにします。

最後に:どれを選ぶ?早見表 + コピペ用テンプレ(CTA)

選び方早見表(Compose / Testcontainers / 動的プロパティ)

やりたいことまずこれひとことで
手元の開発環境を毎回サッと立てたいDocker Compose「外で立てて、アプリはつなぐだけ」
テストを“それ単体で完結”させたいTestcontainers + @ServiceConnection「テストが立てて、Springが勝手につなぐ」
対応外サービス/自作コンテナでも確実にいきたい@DynamicPropertySource(保険)「最後はプロパティを自分で差し込む」

迷ったら基本は テスト=Testcontainers + @ServiceConnection でOKです。

コピペ置き場(Java/Kotlin・JDBC/R2DBCの最小テンプレ)

Java(JDBC)最小テンプレ

@SpringBootTest
@Testcontainers
class DbIT {

  @Container
  @ServiceConnection // URL/ユーザー/パス等をSpringに自動注入
  static PostgreSQLContainer<?> db = new PostgreSQLContainer<>("postgres:16-alpine");

  @Autowired JdbcTemplate jdbc;

  @Test void ok() {
    jdbc.queryForObject("select 1", Integer.class);
  }
}

Kotlin(JDBC)最小テンプレ

@SpringBootTest
@Testcontainers
class DbIT {

  companion object {
    @Container
    @ServiceConnection
    @JvmField // ←static扱いにする保険
    val db = PostgreSQLContainer("postgres:16-alpine")
  }

  @Autowired lateinit var jdbc: JdbcTemplate

  @Test fun ok() {
    jdbc.queryForObject("select 1", Int::class.java)
  }
}

Java(R2DBC)最小テンプレ(※R2DBCを使う人向け)

@SpringBootTest
@Testcontainers
class R2dbcIT {

  @Container
  @ServiceConnection // R2DBC側も自動で接続先が入ります
  static PostgreSQLContainer<?> db = new PostgreSQLContainer<>("postgres:16-alpine");

  @Autowired DatabaseClient client;

  @Test void ok() {
    client.sql("select 1").fetch().one().block();
  }
}

「JDBCとR2DBCが両方作られて困る」時の1行(絞り込み)

@ServiceConnection(type = JdbcConnectionDetails.class) // JDBCだけ作る

依存を入れすぎると候補が増えて混乱しやすいので、「自分はJDBC派?R2DBC派?」を先に決めるのが最短です。

次の一歩(自分のプロジェクトに入れる手順)

  • 依存を入れるspring-boot-testcontainerstestcontainers(+使うDB/Redisモジュール、使うStarter)
  • テンプレを貼る:まずはA案(テストクラスの static / companion object)でOKです
  • テストを1本だけ動かすselect 1(Redisならset/get)で“動いた!”を作ったら勝ちです

この3手順でいけたら、あとはエラー早見に戻って詰まりを潰すだけです。

よくある質問

Q
@ServiceConnection って結局なにをしてくれるんですか?
A

「このコンテナにSpringをつなげてね」の合図です。コンテナから接続情報(URL/host/port/ユーザー/パスワード)を読んで、Springに自動で入れてくれます。

Q
@ServiceConnection を付けたら、コンテナ起動も勝手にやってくれますか?
A

そこは別担当です。起動はTestcontainers@Container など)、つなぐのが@ServiceConnection です。

Q
@DynamicPropertySource はもう不要ですか?
A

対応してるサービスなら、かなり不要になります。ただ、対応外(自作/特殊)は保険として @DynamicPropertySource がまだ役立ちます。

Q
どこに @ServiceConnection を付ければいいですか?
A

基本は2択です。

  • A案:テストクラスの static(Kotlinなら companion object + @JvmField)なコンテナ
  • B案:@TestConfiguration などで @Bean にしたコンテナ

このどっちかじゃないと、Springが見つけられず効きません。

Q
Kotlinだけ動かないんですが、よくある原因は?
A

だいたい @JvmField 付け忘れです。companion object 内のコンテナを “static扱い” にして、Spring/TCが見つけやすくするのがコツです。

Q
「接続先が入ってない(URLがない)」系で落ちます…
A

多い原因は3つです。

  • 依存不足(特に spring-boot-testcontainers
  • @ServiceConnection付ける場所ミス(コンテナ本人じゃない)
  • コンテナが static or @Bean になってない

まずここだけ見直すのが最短です。

Q
ポートって固定じゃないんですか? localhost:5432 に繋がらない…
A

固定じゃないです。コンテナ内部の5432/6379は中の番号で、PC側は毎回別ポートになることがあります。
@ServiceConnection を使うなら、host/portを決め打ちで書かないのが正解です。

Q
Docker Composeを使ってる場合も @ServiceConnection は必要ですか?
A

Composeを外で起動してアプリが繋ぐ運用なら、不要な場面が多いです(普通に spring.datasource.url などを設定します)。
テストを自己完結させたいなら、基本は Testcontainers + @ServiceConnection に寄せるのがラクです。

Q
CI(GitHub Actionsなど)でだけ落ちます。何を疑えばいいですか?
A

まずはこれです👇

  • CIでDockerが使えるか(これが一番多い)
  • リソース不足で起動が遅くないか(タイムアウト系)
  • 並列実行でコンテナが増えすぎてないか

ローカルOK/CIだけNGは、この3つでだいたい説明つきます。

Q
JDBCとR2DBCが両方作られて混乱します。止められますか?
A

止められます。まず Starter入れすぎを疑って、必要な方だけに減らすのが最優先です。
それでも必要なら、@ServiceConnectiontype属性で「作る接続情報」を絞れます(例:JDBCだけ)。

タイトルとURLをコピーしました