具有動態屬性來源的內容組態

Spring TestContext 框架透過 DynamicPropertyRegistry@DynamicPropertySource 註解和 DynamicPropertyRegistrar API 提供對動態屬性的支援。

動態屬性來源基礎架構最初旨在讓基於 Testcontainers 的測試屬性能夠輕鬆地暴露給 Spring 整合測試。但是,這些功能可以與任何形式的外部資源一起使用,這些外部資源的生命週期在測試的 ApplicationContext 之外管理,或者與生命週期由測試的 ApplicationContext 管理的 Bean 一起使用。

優先順序

動態屬性的優先順序高於從 @TestPropertySource、作業系統環境、Java 系統屬性或應用程式使用 @PropertySource 以宣告方式或以程式設計方式新增的屬性來源載入的屬性。因此,動態屬性可用於選擇性地覆寫透過 @TestPropertySource、系統屬性來源和應用程式屬性來源載入的屬性。

DynamicPropertyRegistry

DynamicPropertyRegistry 用於將名稱-值對新增至 Environment。值是動態的,並透過僅在解析屬性時才調用的 Supplier 提供。通常,方法參考用於提供值。以下章節提供如何使用 DynamicPropertyRegistry 的範例。

@DynamicPropertySource

與在類別層級應用的 @TestPropertySource 註解相反,@DynamicPropertySource 可以應用於整合測試類別中的 static 方法,以便將具有動態值的屬性新增至 EnvironmentPropertySources 的集合,用於為整合測試載入的 ApplicationContext

使用 @DynamicPropertySource 註解的整合測試類別中的方法必須是 static,並且必須接受單個 DynamicPropertyRegistry 引數。有關更多詳細資訊,請參閱 DynamicPropertyRegistry 的類別層級 javadoc。

如果您在基底類別中使用 @DynamicPropertySource,並且發現子類別中的測試失敗,因為動態屬性在子類別之間發生變更,則您可能需要使用 @DirtiesContext 註解您的基底類別,以確保每個子類別都獲得自己的 ApplicationContext 以及正確的動態屬性。

以下範例使用 Testcontainers 專案在 Spring ApplicationContext 之外管理 Redis 容器。受管 Redis 容器的 IP 位址和連接埠透過 redis.hostredis.port 屬性提供給測試 ApplicationContext 內的組件。這些屬性可以透過 Spring 的 Environment 抽象存取,或直接注入到 Spring 管理的組件中 – 例如,分別透過 @Value("${redis.host}")@Value("${redis.port}")

  • 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 方法的替代方案,您可以將 DynamicPropertyRegistrar API 的實作註冊為測試 ApplicationContext 中的 Bean。這樣做可讓您支援 @DynamicPropertySource 方法無法實現的其他使用案例。例如,由於 DynamicPropertyRegistrar 本身是 ApplicationContext 中的 Bean,因此它可以與內容中的其他 Bean 互動,並註冊從這些 Bean 來源的動態屬性。

測試 ApplicationContext 中實作 DynamicPropertyRegistrar 介面的任何 Bean 都將在單例預先實例化階段之前自動偵測並主動初始化,並且此類 Bean 的 accept() 方法將使用 DynamicPropertyRegistry 調用,後者代表註冊器執行實際的動態屬性註冊。

與其他 Bean 的任何互動都會導致主動初始化這些其他 Bean 及其依賴項。

以下範例示範如何將 DynamicPropertyRegistrar 實作為 Lambda 運算式,該運算式為 ApiServer Bean 註冊動態屬性。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)
	}
}