Context Configuration with Dynamic Property Sources

Spring TestContext 框架通过 动态 属性提供支持,这些属性可通过 @TestPropertySource 注解和 TestPropertySource API 获得。spring-doc.cadn.net.cn

动态属性源基础设施最初设计用于方便地将来自Testcontainers的测试属性暴露给Spring集成测试。然而,这些功能可以与任何生命周期由测试外部管理的外部资源一起使用,或与生命周期由测试的ApplicationContext管理的bean一起使用。spring-doc.cadn.net.cn

优先级

动态属性的优先级高于从@TestPropertySource、操作系统环境、Java系统属性或应用程序通过使用@PropertySource或编程方式添加的属性源加载的属性。因此,动态属性可以用来有选择地覆盖通过@TestPropertySource、系统属性源和应用程序属性源加载的属性。spring-doc.cadn.net.cn

DynamicPropertyRegistry

使用 DynamicPropertyRegistry 添加 名称-值 对到 Environment 中。 值是动态的,通过 Supplier 提供,该 Supplier 只在属性解析时被调用。通常,使用方法引用来提供值。以下部分提供了如何使用 DynamicPropertyRegistry 的示例。spring-doc.cadn.net.cn

@DynamicPropertySource

与在类级别应用的 @TestPropertySource 注解不同,@DynamicPropertySource 可以应用于集成测试类中的 static 方法,以便向集成测试中加载的 EnvironmentPropertySources 集合中添加具有动态值的属性。spring-doc.cadn.net.cn

集成测试类中使用@DynamicPropertySource注解的方法必须是static,并且必须接受一个DynamicPropertyRegistry类型的参数。有关更多详细信息,请参阅DynamicPropertyRegistry的类级javadoc。spring-doc.cadn.net.cn

如果您在基类中使用 @DynamicPropertySource,并发现子类中的测试失败,因为动态属性在子类之间发生变化,您可能需要使用 @DirtiesContext 注解您的基类,以确保每个子类都拥有自己的 ApplicationContext,并且具有正确的动态属性。spring-doc.cadn.net.cn

以下示例使用Testcontainers项目在Spring ApplicationContext外部管理一个Redis容器。托管的Redis容器的IP地址和端口通过ApplicationContext中的redis.hostredis.port属性提供给测试组件。这些属性可以通过Spring的Environment抽象来访问,或者直接注入到Spring管理的组件中——例如,分别通过@Value("${redis.host}")@Value("${redis.port}")spring-doc.cadn.net.cn

@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中注册DynamicPropertyRegistrar API的实现作为bean。这样做可以支持使用@DynamicPropertySource方法无法实现的其他使用场景。例如,由于DynamicPropertyRegistrar本身是ApplicationContext中的一个bean,它可以与上下文中的其他bean交互并注册源自这些bean的动态属性。spring-doc.cadn.net.cn

测试中的任何bean,只要位于ApplicationContext中并实现了DynamicPropertyRegistrar接口,都将在单例预实例化阶段之前自动被检测到并急切地初始化,此类bean的accept()方法将使用一个DynamicPropertyRegistry调用,该DynamicPropertyRegistry代表注册器执行实际的动态属性注册。spring-doc.cadn.net.cn

与其他bean的任何交互都会导致这些bean及其依赖项的急切初始化。

以下示例展示了如何将DynamicPropertyRegistrar实现为lambda表达式,以便为ApiServer bean注册动态属性。api.url属性可以通过Spring的Environment抽象来访问,或者直接注入到其他Spring管理的组件中——例如,通过@Value("${api.url}"),并且api.url属性的值将从ApiServer bean动态检索。spring-doc.cadn.net.cn

@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)
	}
}