使用动态属性源进行上下文配置

从 Spring Framework 5.2.5 开始,TestContext 框架通过 @DynamicPropertySource 注解提供对动态属性的支持。此注解可用于集成测试,这些测试需要将具有动态值的属性添加到 Environment 中的 PropertySources 集中,以用于为集成测试加载的 ApplicationContext

@DynamicPropertySource 注解及其支持的基础设施最初旨在允许来自 Testcontainers 的基于测试的属性轻松地暴露给 Spring 集成测试。但是,此功能也可以与任何形式的外部资源一起使用,这些资源的生命周期在测试的 ApplicationContext 之外维护。

与在类级别应用的 @TestPropertySource 注解形成对比,@DynamicPropertySource 必须应用于接受单个 DynamicPropertyRegistry 参数的 static 方法,该参数用于向 Environment 添加名称-值对。值是动态的,并通过 Supplier 提供,该 Supplier 仅在解析属性时调用。通常,方法引用用于提供值,如下面的示例所示,该示例使用 Testcontainers 项目在 Spring ApplicationContext 之外管理 Redis 容器。托管 Redis 容器的 IP 地址和端口通过 redis.hostredis.port 属性提供给测试 ApplicationContext 中的组件。可以通过 Spring 的 Environment 抽象访问这些属性,或者直接注入到 Spring 管理的组件中 - 例如,分别通过 @Value("${redis.host}")@Value("${redis.port}")

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

  • 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 ...

}

优先级

动态属性比从 @TestPropertySource、操作系统环境、Java 系统属性或通过使用 @PropertySource 或以编程方式由应用程序声明性添加的属性源加载的属性具有更高的优先级。因此,动态属性可用于选择性地覆盖通过 @TestPropertySource、系统属性源和应用程序属性源加载的属性。