動的プロパティソースを使用したコンテキスト構成

Spring、TestContext、フレームワークは、DynamicPropertyRegistry@DynamicPropertySource アノテーション、および DynamicPropertyRegistrar API を介して動的プロパティのサポートを提供します。

動的プロパティソースインフラストラクチャは、もともと、Testcontainers (英語) ベースのテストのプロパティを Spring 統合テストに簡単に公開できるようにするために設計されました。ただし、これらの機能は、テストの ApplicationContext の外部でライフサイクルが管理される任意の形式の外部リソース、またはテストの ApplicationContext によってライフサイクルが管理される Bean で使用できます。

優先順位

動的プロパティは、@TestPropertySource、オペレーティングシステムの環境、Java システムプロパティ、アプリケーションによって宣言的に @PropertySource を使用して、またはプログラムによって追加されたプロパティソースからロードされたプロパティよりも優先されます。動的プロパティを使用して、@TestPropertySource、システムプロパティソース、アプリケーションプロパティソースを介してロードされたプロパティを選択的にオーバーライドできます。

DynamicPropertyRegistry

DynamicPropertyRegistry は、Environment名前と値のペアを追加するために使用されます。値は動的であり、プロパティが解決されたときにのみ呼び出される Supplier を介して提供されます。通常、メソッド参照を使用して値を指定します。次のセクションでは、DynamicPropertyRegistry の使用方法の例を示します。

@DynamicPropertySource

クラスレベルで適用される @TestPropertySource アノテーションとは対照的に、統合テストクラスの static メソッドに @DynamicPropertySource を適用して、統合テスト用にロードされた ApplicationContext の Environment 内の PropertySources のセットに動的な値を持つプロパティを追加できます。

@DynamicPropertySource でアノテーションが付けられた統合テストクラスのメソッドは、static である必要があり、単一の DynamicPropertyRegistry 引数を受け入れる必要があります。詳細については、DynamicPropertyRegistry のクラスレベルの javadoc を参照してください。

基本クラスで @DynamicPropertySource を使用し、動的プロパティがサブクラス間で変更されるためにサブクラスでのテストが失敗することがわかった場合、各サブクラスが正しい動的プロパティを持つ独自の ApplicationContext を取得するように、基本クラスに @DirtiesContext のアノテーションを付ける必要があります。

次の例では、Testcontainers プロジェクトを使用して、Spring ApplicationContext の外部にある Redis コンテナーを管理します。管理対象の Redis コンテナーの IP アドレスとポートは、redis.host および redis.port プロパティを介して、テストの ApplicationContext 内のコンポーネントで使用できるようになります。これらのプロパティは、Spring の Environment 抽象化を介してアクセスすることも、それぞれ @Value("${redis.host}") および @Value("${redis.port}") を介して Spring 管理コンポーネントに直接挿入することもできます。

  • Java

  • Kotlin

@SpringJUnitConfig(/* ... */)
@Testcontainers
class ExampleIntegrationTests {

	@Container
	static GenericContainer redis =
		new GenericContainer("redis:5.0.3-alpine").withExposedPorts(6379);

	@DynamicPropertySource
	static void redisProperties(DynamicPropertyRegistry registry) {
		registry.add("redis.host", redis::getHost);
		registry.add("redis.port", redis::getFirstMappedPort);
	}

	// tests ...

}
@SpringJUnitConfig(/* ... */)
@Testcontainers
class ExampleIntegrationTests {

	companion object {

		@Container
		@JvmStatic
		val redis: GenericContainer =
			GenericContainer("redis:5.0.3-alpine").withExposedPorts(6379)

		@DynamicPropertySource
		@JvmStatic
		fun redisProperties(registry: DynamicPropertyRegistry) {
			registry.add("redis.host", redis::getHost)
			registry.add("redis.port", redis::getFirstMappedPort)
		}
	}

	// tests ...

}

DynamicPropertyRegistrar

統合テストクラスで @DynamicPropertySource メソッドを実装する代わりに、テストの ApplicationContext 内の Bean として DynamicPropertyRegistrar API の実装を登録できます。これにより、@DynamicPropertySource メソッドでは不可能な追加のユースケースをサポートできます。例: DynamicPropertyRegistrar 自体は ApplicationContext 内の Bean であるため、コンテキスト内の他の Bean と対話し、それらの Bean から取得される動的プロパティを登録できます。

DynamicPropertyRegistrar インターフェースを実装するテストの ApplicationContext 内の Bean はすべて自動的に検出され、シングルトンの事前インスタンス化フェーズの前に積極的に初期化され、そのような Bean の accept() メソッドは、レジストラに代わって実際の動的プロパティ登録を実行する DynamicPropertyRegistry で呼び出されます。

他の Bean とのやり取りにより、他の Bean とその依存関係が積極的に初期化されます。

次の例は、ApiServer Bean の動的プロパティを登録するラムダ式として DynamicPropertyRegistrar を実装する方法を示しています。api.url プロパティは、Spring の Environment 抽象化を介してアクセスするか、他の Spring 管理コンポーネントに直接注入することができます (たとえば、@Value("${api.url}") を介して)。また、api.url プロパティの値は、ApiServer Bean から動的に取得されます。

  • Java

  • Kotlin

@Configuration
class TestConfig {

	@Bean
	ApiServer apiServer() {
		return new ApiServer();
	}

	@Bean
	DynamicPropertyRegistrar apiPropertiesRegistrar(ApiServer apiServer) {
		return registry -> registry.add("api.url", apiServer::getUrl);
	}
}
@Configuration
class TestConfig {

	@Bean
	fun apiServer(): ApiServer {
		return ApiServer()
	}

	@Bean
	fun apiPropertiesRegistrar(apiServer: ApiServer): DynamicPropertyRegistrar {
		return registry -> registry.add("api.url", apiServer::getUrl)
	}
}