Testcontainers導入ガイド:Javaテストにおける環境依存性の悩みを解消する魔法のライブラリ
testcontainers/testcontainers-java
今回解説するのは、Javaのテストの世界で絶大な人気を誇るライブラリ、Testcontainersです。
Testcontainersは、Javaのユニットテストや統合テストをサポートするためのライブラリです。
「推し」がDockerコンテナで動かせるものなら、どんなものでも、テストのためだけに、手軽に立ち上げ、使い終わったら綺麗に片付けてくれる、まるで敏腕マネージャーのような存在です。
Testcontainersは、従来のテスト手法が抱えていたいくつかの大きな悩みを解消してくれます。これは、推し活の「遠征先での宿泊や移動の手配」を自動でやってくれるようなものです!
| 従来のテストの悩み | Testcontainersによる解決 |
| 環境依存性 | 開発者のPC、CI/CD環境など、どこでも同じ本番に近い環境をDockerで再現可能。**「私の環境では動いたのに!」がなくなります。 |
| 外部リソースの準備 | データベースやメッセージキューの準備・起動・データ投入・クリーンアップといった面倒な作業が不要**。テストコードに書くだけで自動実行されます。 |
| テストの隔離 | テストごとに使い捨てのコンテナ(データベースなど)を立ち上げるため、他のテストの影響を受けず、テストの信頼性が向上します。 |
| 本番環境とのギャップ | 組み込みデータベース(例: H2)ではなく、本番で使うのと同じ PostgreSQLやMySQLをテストで利用できるため、本番環境に近いテストが実現します。 |
つまり、Testcontainersを使えば、インフラの準備にかかる時間をゼロにして、アプリケーションロジックのテストという推しのパフォーマンス向上に全力を注げるようになるんです!
Testcontainersを使うには、いくつかの前提条件と、依存関係の設定が必要です。
Dockerが動作している環境が必要です。Testcontainersは、すべての裏方作業をDockerに任せているからです。
使用するテストフレームワーク(JUnit 5が一般的)と、テストしたいコンテナ(ここではPostgreSQLを例に)に合わせて、依存関係を追加します。
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId> <version>最新バージョン</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId> <version>最新バージョン</version>
<scope>test</scope>
</dependency>
dependencies {
// 他の依存関係...
testImplementation 'org.testcontainers:junit-jupiter:最新バージョン'
testImplementation 'org.testcontainers:postgresql:最新バージョン'
}
ここでは、実際のPostgreSQLデータベースを使った統合テストのサンプルコードを紹介します。
このテストでは、@Containerアノテーションを付けるだけで、PostgreSQLのコンテナが自動で起動・停止します。
package com.example.app;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import static org.junit.jupiter.api.Assertions.assertTrue;
// このアノテーションを付けると、テストクラス内でTestcontainersが有効になります
@Testcontainers
class UserRepositoryIntegrationTest {
// ★ 推しのコンテナを定義!
// WITH_USERNAME, WITH_PASSWORD, WITH_DATABASEはPostgreSQLの特別な設定メソッド
@Container
private static final PostgreSQLContainer<?> postgresContainer = new PostgreSQLContainer<>("postgres:13-alpine")
.withDatabaseName("testdb")
.withUsername("testuser")
.withPassword("testpass");
@Test
void should_insert_and_retrieve_data() throws Exception {
// Testcontainersが提供する動的なJDBC URL、ユーザー名、パスワードを取得
String jdbcUrl = postgresContainer.getJdbcUrl();
String username = postgresContainer.getUsername();
String password = postgresContainer.getPassword();
// データベースに接続
try (Connection connection = DriverManager.getConnection(jdbcUrl, username, password);
Statement statement = connection.createStatement()) {
// テーブル作成
statement.execute("CREATE TABLE users (id SERIAL PRIMARY KEY, name VARCHAR(100))");
// データ挿入
statement.executeUpdate("INSERT INTO users (name) VALUES ('推しエンジニア')");
// データ取得
ResultSet rs = statement.executeQuery("SELECT name FROM users WHERE name = '推しエンジニア'");
// 検証!
assertTrue(rs.next(), "データが正しく挿入・取得されること");
}
// finallyブロックでclose処理を書く必要はありません。
// テスト終了後、Testcontainersが自動でコンテナを破棄してくれます!
}
}
@Testcontainers
クラスに付けて、Testcontainersを使いますよ、と宣言します。
@Container
フィールドに付けて、このインスタンスがDockerコンテナですよ、と宣言します。
new PostgreSQLContainer<>("postgres:13-alpine")
使用したいDockerイメージを指定します。バージョンも指定できるのが嬉しいですね。
getJdbcUrl() など
接続情報は、コンテナが起動するたびに動的に生成されます。この動的な情報を使うことで、テストコードが環境から完全に分離されます。
このコードを実行すれば、テストのためだけにPostgreSQLが立ち上がり、テストが動いて、テストが終わったら静かに消えていく...という、完璧にクリーンな環境でのテストが実現します。