Testcontainersを使用したSpring Bootアプリケーションのテストと開発

スプリングブート 3。1.0 は、統合テストの記述を容易にするだけでなく、ローカル開発を容易にする Testcontainers の優れたサポートを導入しました。

Spring Bootアプリケーションのテストとテストコンテナによる開発

「Clone & Run」開発者エクスペリエンス

アプリケーションを実行する前にローカルでセットアップするために必要な手動手順の長いリストを含むドキュメントを管理する時代は終わりました。 Dockerがアプリケーションをインストールすることで、依存関係が容易になりました。 しかし、アプリケーションの依存関係をDockerコンテナとして手動でスピンアップするために、オペレーティングシステムに基づいて異なるバージョンのスクリプトを維持する必要がありました。

Spring Boot 3に追加された Testcontainers のサポートにより、 .1.0, 開発者は、リポジトリをクローンしてアプリケーションを実行するだけで済みます。 データベース、メッセージブローカーなどのすべてのアプリケーションの依存関係は、アプリケーションの実行時に自動的に開始するように構成できます。

Testcontainers を初めて使用する場合は、「 Java Spring Boot プロジェクトの Testcontainers 入門 」ガイドを参照して、Testcontainers を使用して Spring Boot アプリケーションをテストする方法を学習してください。

ServiceConnections を使用した統合テストの簡素化

Spring Boot 3より前は .1.0, Testcontainers によって起動されたコンテナーから取得した動的プロパティを次のように設定するために使用する @DynamicPropertySource 必要がありました。

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Testcontainers
class CustomerControllerTest {

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

   @DynamicPropertySource
   static void configureProperties(DynamicPropertyRegistry registry) {
       registry.add("spring.datasource.url", postgres::getJdbcUrl);
       registry.add("spring.datasource.username", postgres::getUsername);
       registry.add("spring.datasource.password", postgres::getPassword);
   }

   // your tests
}

次に、Spring Boot 3。1.0 ServiceConnection の新しい概念が導入されました。これにより、サポートするコンテナーに必要な Spring Boot プロパティが自動的に構成されます。

まず、 テスト 依存関係として追加 spring-boot-testcontainers します。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-testcontainers</artifactId>
    <scope>test</scope>
</dependency>

これで、前の例を明示的に設定spring.datasource.urlせずに追加@ServiceConnectionすることで書き直すことができます。spring.datasource.username, そして spring.datasource.password 、その @DynamicPropertySource アプローチを使用します。

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Testcontainers
class CustomerControllerTest {

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

   // your tests
}

データソースのプロパティを明示的に登録していないことに注意してください。

このサポートは@ServiceConnection、リレーショナルデータベースだけでなく、KafkaRabbitMQ、RedisMongoDBElasticSearch、Neo4jなど、一般的に使用される他の多くの依存関係でも機能します。サポートサービスの完全なリストについては、 公式ドキュメントを参照してください。

また、すべてのコンテナーの依存関係を 1 つの TestConfiguration クラスで定義し、それを統合テストにインポートすることもできます。

たとえば、アプリケーションで PostgresKafka を使用しているとします。 その後、次のようにという ContainersConfig クラスを作成できます。

@TestConfiguration(proxyBeanMethods = false)
public class ContainersConfig {

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

   @Bean
   @ServiceConnection
   public KafkaContainer kafkaContainer() {
       return new KafkaContainer(
                   DockerImageName.parse("confluentinc/cp-kafka:7.2.1"));
   }
}

最後に、次のようにテストにインポート ContainersConfig できます。

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Import(ContainersConfig.class)
class ApplicationTests {

   //your tests
}

ServiceConnection をサポートしていないコンテナーを使用する方法

アプリケーションでは、専用の Testcontainers モジュールや Spring Boot のすぐに使用できる ServiceConnection サポートがない依存関係を使用する必要がある場合があります。 Testcontainers GenericContainer を使用して、 を使用してプロパティ DynamicPropertyRegistryを登録できますので、ご安心ください。

たとえば、電子メール機能のテストに Mailhog を使用できます。 この場合、Testcontainers GenericContainer を使用して、次のように Spring Boot の電子メール プロパティを登録できます。

@TestConfiguration(proxyBeanMethods = false)
public class ContainersConfig {

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

   @Bean
   @ServiceConnection
   public KafkaContainer kafkaContainer() {
       return new KafkaContainer(
                    DockerImageName.parse("confluentinc/cp-kafka:7.2.1"));
   }

   @Bean
   public GenericContainer mailhogContainer(DynamicPropertyRegistry registry) {
       GenericContainer container = new GenericContainer("mailhog/mailhog")
                                            .withExposedPorts(1025);
       registry.add("spring.mail.host", container::getHost);
       registry.add("spring.mail.port", container::getFirstMappedPort);
       return container;
   }
}

これまで見てきたように、任意のコンテナー化されたサービスを使用して、アプリケーションのプロパティを登録できます。

Testcontainers を使用したローカル開発

前のセクションでは、Testcontainerを使用してSpring Bootアプリケーションをテストする方法を学びました。 Spring Boot 3を使用します。1.0 Testcontainers のサポートでは、開発時に Testcontainers を使用してアプリケーションをローカルで実行することもできます。

これを行うには、 次のように src/test/java の下のテストクラスパスにクラスを作成します TestApplication 。

import org.springframework.boot.SpringApplication;

public class TestApplication {
   public static void main(String[] args) {
       SpringApplication
         .from(Application::main) //Application is main entrypoint class
         .with(ContainersConfig.class)
         .run(args);
   }
}

構成.with(...)クラスContainersConfigを使用して、アプリケーション起動ツールにアタッチしたことを確認します。

これで、IDEから実行できます TestApplication 。 で ContainersConfig 定義されているすべてのコンテナーが自動的に起動され、プロパティが構成されます。

次のように、Maven または Gradle ビルドツールを使用して実行 TestApplication することもできます。

./mvnw spring-boot:test-run //Maven
./gradlew bootTestRun //Gradle

開発時に Testcontainers で DevTools を使用する

ここまでで、ローカル開発に Testcontainers を使用する方法を学びました。 ただし、このセットアップの 1 つの課題は、アプリケーションが変更されてビルドがトリガーされるたびに、既存のコンテナーが破棄され、新しいコンテナーが作成されることです。 これにより、アプリケーションの再起動間でデータが遅くなったり、失われたりする可能性があります。

Spring Bootは、コードの変更時にアプリケーションを更新することで開発者エクスペリエンスを向上させるための開発ツールを提供します。 devtools によって提供されるアノテーションを使用して @RestartScope 、特定の Bean を再作成する代わりに再利用することを示すことができます。

まず、 spring-boot-devtools 依存関係を次のように追加しましょう。

<!-- For Maven -->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-devtools</artifactId>
   <scope>runtime</scope>
   <optional>true</optional>
</dependency>

<!-- For Gradle -->
testImplementation "org.springframework.boot:spring-boot-devtools"

次に、次のようにBean定義ContainersConfigに注釈を追加します@RestartScope

@TestConfiguration(proxyBeanMethods = false)
public class ContainersConfig {

   @Bean
   @ServiceConnection
   @RestartScope
   public PostgreSQLContainer<?> postgreSQLContainer() {
       return new PostgreSQLContainer<>("postgres:15.2-alpine");
   }

   @Bean
   @ServiceConnection
   @RestartScope
   public KafkaContainer kafkaContainer() {
       return new KafkaContainer(
                DockerImageName.parse("confluentinc/cp-kafka:7.2.1"));
   }

   ...
}

これで、アプリケーション コードを変更し、ビルドがトリガーされた場合、アプリケーションは再起動されますが、既存のコンテナーが使用されます。

注意:Eclipseはコードの変更が保存されると自動的にビルドをトリガーしますが、IntelliJ IDEAではビルドを手動でトリガーする必要があります。

結論

最新のソフトウェア開発では、増大するビジネスニーズに対応するために、多くのテクノロジーとツールを使用します。 これにより、開発環境のセットアップが大幅に複雑になりました。 開発者エクスペリエンスの向上は、もはや 「あればいい」 というものではなく、 必要不可欠なものです。

この開発者エクスペリエンスを改善するために、Spring Boot 3.1.0 Testcontainers の標準サポートが追加されました。 Spring Boot と Testcontainers の統合は、ローカルの Docker、CI、 および Testcontainers Cloud でもシームレスに機能します。

これは、テストだけでなく、ローカル開発にも影響を与える変革です。 そして開発者は、 クローン &ランの哲学を現実のものにする体験を期待することができます。

さらに詳しく