使用动态属性源进行上下文配置
Spring TestContext Framework 通过 DynamicPropertyRegistry、@DynamicPropertySource 注解和 DynamicPropertyRegistrar API 支持动态属性。
|
动态属性源基础设施最初设计用于将基于 Testcontainers 的测试中的属性轻松暴露给 Spring 集成测试。然而,这些功能也可用于任何生命周期在测试的 |
优先级
动态属性的优先级高于从 @TestPropertySource、操作系统环境变量、Java 系统属性,或应用通过声明式(使用 @PropertySource)或程序化方式添加的属性源加载的属性。因此,动态属性可用于选择性地覆盖通过 @TestPropertySource、系统属性源和应用属性源加载的属性。
DynamicPropertyRegistry
DynamicPropertyRegistry 用于向 Environment 添加键值对。这些值是动态的,通过一个 Supplier 提供,该 Supplier 仅在属性被解析时调用。通常使用方法引用来提供值。以下各节提供了如何使用 DynamicPropertyRegistry 的示例。
@DynamicPropertySource
与应用于类级别的 @TestPropertySource 注解不同,@DynamicPropertySource 可以应用于集成测试类中的 static 方法,以便将具有动态值的属性添加到为该集成测试加载的 ApplicationContext 的 Environment 中的 PropertySources 集合中。
集成测试类中用 @DynamicPropertySource 注解的方法必须是 static 的,并且必须接受一个 DynamicPropertyRegistry 参数。有关更多详细信息,请参阅 DynamicPropertyRegistry 的类级别 javadoc。
|
如果在基类中使用 |
以下示例使用 Testcontainers 项目来管理 Spring ApplicationContext 外部的 Redis 容器。通过 redis.host 和 redis.port 属性,托管 Redis 容器的 IP 地址和端口可供测试的 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 都将在单例预实例化阶段之前自动检测并急切初始化,并且将使用代表注册器执行实际动态属性注册的 DynamicPropertyRegistry 调用这些 bean 的 accept() 方法。
| 与任何其他 bean 的交互都会导致这些其他 bean 及其依赖项的急切初始化。 |
以下示例演示了如何将 DynamicPropertyRegistrar 实现为一个 lambda 表达式,该表达式为 ApiServer bean 注册一个动态属性。可以通过 Spring 的 Environment 抽象访问 api.url 属性,或直接注入到其他 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)
}
}