- JAVA
- Spring
Testcontainersの使い方|Spring BootでPostgreSQL
Testcontainers は、テストの開始時に Docker で本物の PostgreSQL などを起動し、終わったら片付けてくれる Java のライブラリです。 H2 のような代わりの DB ではなく、本番と同じ DB でテストできます。Spring Boot では、コンテナに @ServiceConnection を付けるだけで、spring.datasource.url などの接続先が自動で設定されます。
@SpringBootTest
@Testcontainers
class UserRepositoryTest {
@Container
@ServiceConnection
static PostgreSQLContainer postgres = new PostgreSQLContainer("postgres:17-alpine");
@Autowired UserRepository repository;
@Test
void save() { … }
}
| 書き方 | 接続先の設定 | 向いている場面 |
|---|---|---|
@Container + @ServiceConnection |
Boot が自動で設定 | ふつうはこれ。PostgreSQL・MySQL・Redis など対応のもの |
@Container + @DynamicPropertySource |
自分で registry.add(…) |
対応していないサービス、独自の設定名を使う |
@Bean + @ServiceConnection(@TestConfiguration) |
Boot が自動で設定 | 複数のテストクラスで共有。開発時の起動にも使える |
確認環境:Testcontainers 2.0.5(GitHub のタグで最新の版。Spring Boot 4.1.1 が管理する版と同じ)と Spring Boot 4.1.1。依存の名前・クラス名・既定値・エラー文は、Testcontainers のタグ 2.0.5 のソースとドキュメント、2.0.0 のリリースノート、Spring Boot のタグ v4.1.1 のソース(spring-boot-testcontainers・AutoConfigureTestDatabase)とドキュメントで確かめました。GitHub Actions の Ubuntu ランナーに Docker が入っていることは、GitHub の runner-images の一覧で確かめました(確認日 2026年10月13日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Testcontainersの使い方|Spring BootでPostgreSQL」
Testcontainers 2.0 で変わったこと
2.0 では、依存の名前とパッケージが変わりました。 1.x の記事のコードをそのまま写すと、依存が見つからないかクラスが非推奨になります。
| 1.x | 2.0 | |
|---|---|---|
| PostgreSQL の依存 | org.testcontainers:postgresql |
org.testcontainers:testcontainers-postgresql |
| JUnit 5 の依存 | org.testcontainers:junit-jupiter |
org.testcontainers:testcontainers-junit-jupiter |
| PostgreSQL のクラス | org.testcontainers.containers.PostgreSQLContainer<?> |
org.testcontainers.postgresql.PostgreSQLContainer(型引数なし) |
| 旧クラス | — | 残っているが @Deprecated |
| 引数なしのコンストラクタ | あった(既定の版を使う) | 削除。イメージ名を必ず書く |
JUnit 4(@Rule) |
対応 | 削除 |
リリースノートには「すべてのモジュールに testcontainers- の接頭辞が付く」「コンテナのクラスは org.testcontainers.<モジュール名> に移る」「JUnit 4 のサポートを削除」とあります。PostgreSQL のコンテナの既定のユーザー名・パスワード・DB 名は、ソースではどれも test です。
依存(Spring Boot 4.1.1)
版は Spring Boot が管理する(Testcontainers の BOM を読み込む)ので、書きません。
build.gradle.kts
plugins {
java
id("org.springframework.boot") version "4.1.1"
id("io.spring.dependency-management") version "1.1.7"
}
java { toolchain { languageVersion = JavaLanguageVersion.of(21) } }
repositories { mavenCentral() }
dependencies {
implementation("org.springframework.boot:spring-boot-starter-webmvc")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-flyway")
implementation("org.flywaydb:flyway-database-postgresql")
runtimeOnly("org.postgresql:postgresql")
testImplementation("org.springframework.boot:spring-boot-starter-data-jpa-test")
testImplementation("org.springframework.boot:spring-boot-testcontainers") // @ServiceConnection
testImplementation("org.testcontainers:testcontainers-junit-jupiter") // @Testcontainers・@Container
testImplementation("org.testcontainers:testcontainers-postgresql") // PostgreSQLContainer
}
tasks.test { useJUnitPlatform() }
pom.xml(dependencies の部分)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-testcontainers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-postgresql</artifactId>
<scope>test</scope>
</dependency>
| 依存 | 何のため |
|---|---|
spring-boot-testcontainers |
@ServiceConnection と、コンテナを Bean として扱うしくみ(Boot のドキュメントに「service connection を使うには test の依存に足す」とある) |
testcontainers-junit-jupiter |
JUnit 5 の拡張(@Testcontainers・@Container) |
testcontainers-postgresql |
PostgreSQLContainer |
Boot 4 のテスト用 starter(spring-boot-starter-data-jpa-test など)は、関連記事「Spring Boot starter一覧|用途別の選び方とBoot 4の新旧対応表」で、Gradle の書き方は、関連記事「build.gradle書き方|Groovy/Kotlin DSL対訳と依存設定一覧」で扱っています。
書き方①:@Testcontainers + @Container + @ServiceConnection
テストの対象です。
// src/main/java/com/example/demo/User.java
@Entity
@Table(name = "users")
public class User {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
protected User() {}
public User(String name, String email) { this.name = name; this.email = email; }
public Long getId() { return id; }
public String getName() { return name; }
public String getEmail() { return email; }
}
// src/main/java/com/example/demo/UserRepository.java
public interface UserRepository extends JpaRepository<User, Long> {
@Query(value = "SELECT * FROM users WHERE name ILIKE CONCAT('%', :keyword, '%')", nativeQuery = true)
List<User> searchByName(@Param("keyword") String keyword); // PostgreSQL の ILIKE
}
テーブルは Flyway で作ります(src/main/resources/db/migration/V1__create_users.sql)。Flyway の使い方は、関連記事「Flywayの使い方|Spring Boot・命名規則・失敗時の直し方」で扱っています。
// src/test/java/com/example/demo/UserRepositoryTest.java
package com.example.demo;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.data.jpa.test.autoconfigure.DataJpaTest;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.postgresql.PostgreSQLContainer;
@DataJpaTest
@Testcontainers
class UserRepositoryTest {
@Container // テストクラスの前に起動、後に停止(static なのでクラス内で共有)
@ServiceConnection // 接続先を Boot に教える
static PostgreSQLContainer postgres = new PostgreSQLContainer("postgres:17-alpine");
@Autowired
UserRepository repository;
@Test
void ILIKEで大文字小文字を区別せずに探せる() {
repository.save(new User("Taro Yamada", "taro@example.com"));
repository.save(new User("Hanako", "hanako@example.com"));
assertThat(repository.searchByName("taro"))
.extracting(User::getName)
.containsExactly("Taro Yamada");
}
}
@DataJpaTest は、ふつうは DB を組み込みの DB(H2 など)に差し替えます。 ただし Boot 4.1.1 の @AutoConfigureTestDatabase の既定は Replace.NON_TEST で、ソースのコメントには「@ServiceConnection を付けた Testcontainers の DB」「@DynamicPropertySource で設定した spring.datasource.url」はテスト用の DB とみなして差し替えないとあります。そのため、@AutoConfigureTestDatabase(replace = NONE) は書かなくても PostgreSQL に繋がります。
ILIKE は PostgreSQL の書き方です。 このように「本番の DB でしか動かない SQL」を確かめられるのが、Testcontainers を使う一番の理由です。AssertJ の extracting は、関連記事「AssertJの使い方|assertThatの早見表(文字列・リスト・例外・日付)」で扱っています。
@Container を static にするかどうか
Testcontainers のドキュメントでは、次のように説明されています。
| フィールド | 起動と停止 | 速さ |
|---|---|---|
static |
クラスの最初のテストの前に1回起動、最後のテストの後に停止 | 速い(クラスで共有) |
| static でない | テストメソッドごとに起動と停止 | 遅い |
@ServiceConnection を付けるフィールドは static にします。 Boot のソースでは、static でないと次のエラーになります。
@ServiceConnection field 'postgres' must be static
また、Testcontainers のドキュメントには、この拡張は「テストを順番に実行する前提で確かめていて、並列実行では動きが保証されない」とあります。
書き方②:@DynamicPropertySource(自分で設定する)
@ServiceConnection が使えないとき(対応していないサービス、独自の設定名など)は、@DynamicPropertySource で設定を足します。
@SpringBootTest
@Testcontainers
class UserApiTest {
@Container
static PostgreSQLContainer postgres = new PostgreSQLContainer("postgres:17-alpine")
.withDatabaseName("app")
.withUsername("app")
.withPassword("secret");
@DynamicPropertySource
static void props(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username", postgres::getUsername);
registry.add("spring.datasource.password", postgres::getPassword);
registry.add("app.feature.search-enabled", () -> "true"); // 独自の設定も足せる
}
// テスト …
}
@ServiceConnection と @DynamicPropertySource の違い
@ServiceConnection |
@DynamicPropertySource |
|
|---|---|---|
| 書く量 | 注釈1つ | 設定名を1つずつ registry.add |
| 何を設定するか | JdbcConnectionDetails などの Bean を作る |
Spring の Environment に設定値を足す |
| 優先 | Boot のドキュメントでは「接続に関する設定値より優先」 | ふつうの設定値と同じ並びで効く |
| 対応しているもの | Boot に用意があるもの(PostgreSQL・MySQL・MariaDB・Oracle・SQL Server・MongoDB・Redis・Kafka・RabbitMQ・Elasticsearch など) | 何でも |
@DataJpaTest で差し替えられない |
される(テスト用 DB とみなす) | spring.datasource.url を足した場合はされる |
| メソッド | — | static にする |
Boot のドキュメントでは、@DynamicPropertySource は「service connection より少し冗長だが、より柔軟な方法」と書かれています。PostgreSQLContainer に @ServiceConnection を付けると、JDBC 用(JdbcConnectionDetails)と R2DBC 用(R2dbcConnectionDetails)の両方が作られます。片方だけにしたいときは、@ServiceConnection(type = JdbcConnectionDetails.class) のように type を指定します。
書き方③:コンテナを Bean にして共有する
Boot のドキュメントは、テストクラスをまたいで同じコンテナを使う場合、コンテナを Spring の Bean にする書き方をすすめています。 JUnit の拡張で管理すると、クラスの終わりにコンテナが止まりますが、Spring はテスト間でアプリケーションコンテキスト(Bean の入れ物)をキャッシュして使い回すため、止まったコンテナを参照して失敗することがあるからです。
// src/test/java/com/example/demo/TestcontainersConfiguration.java
package com.example.demo;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.context.annotation.Bean;
import org.testcontainers.postgresql.PostgreSQLContainer;
@TestConfiguration(proxyBeanMethods = false)
public class TestcontainersConfiguration {
@Bean
@ServiceConnection
PostgreSQLContainer postgresContainer() {
return new PostgreSQLContainer("postgres:17-alpine");
}
}
@SpringBootTest
@Import(TestcontainersConfiguration.class)
class OrderServiceTest {
// @Testcontainers も @Container も要らない
}
Boot のドキュメントによると、Bean にしたコンテナは「ほかの Bean より先に起動し、ほかの Bean を片付けた後に停止」し、同じアプリケーションコンテキストを使うテストの間で1つのコンテナが使い回されます。
開発時のアプリ起動にも使う
同じ設定で、ローカルでアプリを起動するときにも PostgreSQL を立てられます。src/test/java に起動用のクラスを置きます。
// src/test/java/com/example/demo/TestDemoApplication.java
package com.example.demo;
import org.springframework.boot.SpringApplication;
public class TestDemoApplication {
public static void main(String[] args) {
SpringApplication.from(DemoApplication::main)
.with(TestcontainersConfiguration.class)
.run(args);
}
}
Boot のドキュメントでは、Gradle の bootTestRun、Maven の spring-boot:test-run で起動できるとあります。Docker Compose で開発用の DB を立てる方法は、関連記事「Docker Composeの使い方|compose.yamlの書き方と開発環境の作り方」で扱っています。
コンテナの再利用(reuse)
テストを実行するたびにコンテナを起動すると、数秒かかります。 ローカルで何度も実行するときは、コンテナを止めずに使い回す「reuse」という機能があります。ドキュメントでは Experimental(試験的) と書かれています。
static PostgreSQLContainer postgres = new PostgreSQLContainer("postgres:17-alpine")
.withReuse(true);
static {
postgres.start(); // 自分で start() する。stop() は呼ばない
}
# ~/.testcontainers.properties(ホームディレクトリ)
testcontainers.reuse.enable=true
| 条件 | 内容(Testcontainers のドキュメント) |
|---|---|
| 有効にする場所 | 環境変数 TESTCONTAINERS_REUSE_ENABLE=true か、~/.testcontainers.properties |
| 有効にできない場所 | クラスパス上の properties ファイル(src/test/resources など) |
| 起動のしかた | start() を自分で呼ぶ。@Container や try-with-resources で止めない |
| 再利用される条件 | コンテナの設定がまったく同じ |
| CI | 向いていない(ドキュメントに明記) |
withReuse(true) を書いても、環境で有効にしていないと、次の警告が出て、ふつうのコンテナとして動きます(ソースの文言)。
Reuse was requested but the environment does not support the reuse of containers
To enable reuse of containers, you must set 'testcontainers.reuse.enable=true' in a file located at /home/<user>/.testcontainers.properties
再利用したコンテナには前回のデータが残ります。 テストごとにデータを消す(@Transactional でロールバック、@Sql で TRUNCATE など)ようにします。@DataJpaTest は、テストごとにトランザクションをロールバックします。
GitHub Actions で実行する
GitHub が用意している Ubuntu のランナー(ubuntu-latest)には Docker が入っているので、追加の設定なしで Testcontainers が動きます。 GitHub の runner-images の一覧(Ubuntu 24.04)には、Docker Client・Docker Server 28.0.4、Docker Compose 2.38.2 が載っています。
# .github/workflows/test.yml
name: test
on:
push:
branches: [main]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '21'
- uses: gradle/actions/setup-gradle@v4
- name: Test
run: ./gradlew test
- name: Upload test report
if: failure()
uses: actions/upload-artifact@v4
with:
name: test-report
path: build/reports/tests/test
Maven なら run: ./mvnw -B verify にします。
| 注意点 | 内容 |
|---|---|
| ランナーの OS | Linux のランナーを使う(Docker が動く) |
| イメージの取得回数 | Docker Hub は取得回数に上限がある。Testcontainers のドキュメントにも「image registry rate limiting」の章がある |
withReuse(true) |
CI では使わない(ドキュメントで非推奨) |
| 時間 | イメージの取得とコンテナの起動の分だけ、テストが長くなる |
GitHub Actions の基本は、関連記事「CI/CDとは|GitHub Actionsで最初のパイプラインを作るまで」で、キャッシュの設定は、関連記事「GitHub Actionsのキャッシュとmatrix|コピペ用YAMLとキー設計」で扱っています。
エラー文と原因の表
エラー文は、Testcontainers 2.0.5 と Spring Boot 4.1.1 のソースにある文言です。
| エラー文(原文) | 原因 | 対処 |
|---|---|---|
Could not find a valid Docker environment. Please see logs and check configuration |
Docker が起動していない、ソケットに接続できない | Docker を起動する。ログの「Attempted configurations were:」で試した接続先を見る |
Previous attempts to find a Docker environment failed. Will not retry. Please see logs and check configuration |
上のエラーの後、同じ JVM の2回目以降のテスト | 最初のエラーを直す |
Container startup failed for image postgres:17-alpine |
コンテナの起動に失敗(イメージが取れない・すぐ落ちた) | 原因(cause)の例外を見る |
Could not create/start container |
上と同じく起動の失敗 | Docker のログ、ディスクの空き、メモリ |
Timed out waiting for container port to open (…) |
ポートが開くのを待つ間に時間切れ | Docker の負荷、起動待ちの時間 |
Wait strategy failed. Container exited with code … |
起動待ちの間にコンテナが終了 | 環境変数や設定の誤り。コンテナのログを見る |
Wait strategy failed. Container crashed with out-of-memory (OOMKilled) |
メモリ不足で強制終了 | Docker に割り当てるメモリを増やす |
Failed to verify that image '…' is a compatible substitute for '…'. |
postgres 以外の名前のイメージ(社内ミラーなど)を指定 |
DockerImageName.parse("…").asCompatibleSubstituteFor("postgres") |
@ServiceConnection field 'postgres' must be static |
@ServiceConnection のフィールドが static でない |
static を付ける |
No ConnectionDetails found for source '…'. You may need to add a 'name' to your @ServiceConnection annotation |
GenericContainer などで、Boot が何のサービスか分からない |
@ServiceConnection(name = "redis") のように名前を書く |
Reuse was requested but the environment does not support the reuse of containers |
withReuse(true) だが環境で有効にしていない |
~/.testcontainers.properties に testcontainers.reuse.enable=true |
Docker が無い環境(一部の CI や、Docker を入れていない PC)でテストを飛ばしたいときは、@Testcontainers(disabledWithoutDocker = true) にします。 ソースでは、Docker が無いと disabledWithoutDocker is true and Docker is not available という理由でテストが「無効(disabled)」になり、失敗ではなくスキップとして扱われます。
Docker の接続そのもののエラーは、関連記事「Cannot connect to the Docker daemonの原因と直し方」で扱っています。
Docker Desktop 以外を使うとき
Testcontainers のドキュメントでは、対応する実行環境として Docker Desktop、Linux の Docker Engine、Testcontainers Cloud が挙がっています。それ以外(Colima・Podman・リモートの Docker)は、接続先の設定が要ることがあります。
| 設定 | 意味 |
|---|---|
DOCKER_HOST(環境変数) |
Docker の接続先。既定は unix:///var/run/docker.sock |
docker.host(~/.testcontainers.properties) |
上と同じ |
TESTCONTAINERS_HOST_OVERRIDE(環境変数) |
コンテナに繋ぐときのホスト名を上書き |
TESTCONTAINERS_RYUK_DISABLED=true |
後片付け用のコンテナ(Ryuk)を使わない。特権コンテナが使えない環境向け |
よくある質問
Q. H2 でテストすればよいのでは?
A. H2 は PostgreSQL と SQL の方言や型が違うため、ILIKE・ON CONFLICT・jsonb などの PostgreSQL の機能は確かめられません。Testcontainers なら、本番と同じ版の PostgreSQL で確かめられます。代わりに、コンテナの起動の時間がかかります。UPSERT の DB ごとの違いは、関連記事「SQL UPSERTの書き方|PostgreSQL・MySQL・MERGE構文比較表」で扱っています。
Q. PostgreSQL の版はどう選びますか。
A. 本番と同じメジャー版を、タグで指定します(postgres:17-alpine など)。latest にすると、ある日から別の版でテストが動くことになります。
Q. テストが遅いです。
A. コンテナを static にしてクラスで共有する、Bean にしてアプリケーションコンテキストごとに共有する、ローカルでは reuse を使う、の順に効きます。テストクラスごとに設定(@MockitoBean の有無など)が違うと、コンテキストが別になり、コンテナも別に起動します。
Q. Controller のテストにも要りますか。
A. @WebMvcTest で Controller だけを確かめるテストは、DB を使わないので要りません。DB まで通すテストに使います。使い分けは、関連記事「Spring Bootテスト入門|@WebMvcTestとMockMvcの使い分け」で扱っています。
まとめ
- 2.0 は依存が
testcontainers-postgresql・testcontainers-junit-jupiter、クラスはorg.testcontainers.postgresql.PostgreSQLContainer - Boot では
spring-boot-testcontainersを足し、static なコンテナに@ServiceConnection @ServiceConnectionが使えないものは@DynamicPropertySource- 複数のテストクラスで共有するなら、
@TestConfigurationの@Beanにする(Boot のドキュメントのおすすめ) - reuse はローカル専用。
~/.testcontainers.propertiesで有効にする - GitHub Actions は
ubuntu-latestでそのまま動く。Docker が無い環境はdisabledWithoutDocker = true
参考・出典
確認日はいずれも 2026年10月13日です。
- Testcontainers for Java ドキュメント:https://java.testcontainers.org/
- Testcontainers(GitHub、ソースとドキュメント・タグ 2.0.5):https://github.com/testcontainers/testcontainers-java
- Testcontainers 2.0.0 リリースノート:https://github.com/testcontainers/testcontainers-java/releases/tag/2.0.0
- Testcontainers ドキュメント「Reusable Containers」:https://java.testcontainers.org/features/reuse/
- Spring Boot ドキュメント「Testcontainers」:https://docs.spring.io/spring-boot/reference/testing/testcontainers.html
- Spring Boot ドキュメント「Development-time Services」:https://docs.spring.io/spring-boot/reference/features/dev-services.html
- Spring Boot ソース(タグ v4.1.1):https://github.com/spring-projects/spring-boot/tree/v4.1.1
- GitHub Actions runner-images(Ubuntu 24.04):https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2404-Readme.md
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Testcontainersの使い方|Spring BootでPostgreSQL」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。