This version is still in development and is not considered stable yet. For the latest stable version, please use Spring Framework 6.1.7!

Context Configuration with Dynamic Property Sources

The Spring TestContext Framework provides support for dynamic properties via the @DynamicPropertySource annotation and the DynamicPropertyRegistry.

The @DynamicPropertySource annotation and its supporting infrastructure were originally designed to allow properties from Testcontainers based tests to be exposed easily to Spring integration tests. However, this feature may be used with any form of external resource whose lifecycle is managed outside the test’s ApplicationContext or with beans whose lifecycle is managed by the test’s ApplicationContext.

In contrast to the @TestPropertySource annotation that is applied at the class level, @DynamicPropertySource can be applied to static methods in integration test classes or to @Bean methods in test @Configuration classes in order to add properties with dynamic values to the set of PropertySources in the Environment for the ApplicationContext loaded for the integration test.

A DynamicPropertyRegistry is used to add name-value pairs to the Environment. Values are dynamic and provided via a Supplier which is only invoked when the property is resolved. Typically, method references are used to supply values.

Methods in integration test classes that are annotated with @DynamicPropertySource must be static and must accept a single DynamicPropertyRegistry argument.

@Bean methods annotated with @DynamicPropertySource may either accept an argument of type DynamicPropertyRegistry or access a DynamicPropertyRegistry instance autowired into their enclosing @Configuration class. Note, however, that @Bean methods which interact with a DynamicPropertyRegistry are not required to be annotated with @DynamicPropertySource unless they need to enforce eager initialization of the bean within the context. See the class-level javadoc for DynamicPropertyRegistry for details.

If you use @DynamicPropertySource in a base class and discover that tests in subclasses fail because the dynamic properties change between subclasses, you may need to annotate your base class with @DirtiesContext to ensure that each subclass gets its own ApplicationContext with the correct dynamic properties.

The following example uses the Testcontainers project to manage a Redis container outside of the Spring ApplicationContext. The IP address and port of the managed Redis container are made available to components within the test’s ApplicationContext via the redis.host and redis.port properties. These properties can be accessed via Spring’s Environment abstraction or injected directly into Spring-managed components – for example, via @Value("${redis.host}") and @Value("${redis.port}"), respectively.

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

}

The following example demonstrates how to use DynamicPropertyRegistry and @DynamicPropertySource with a @Bean method. The api.url property can be accessed via Spring’s Environment abstraction or injected directly into other Spring-managed components – for example, via @Value("${api.url}"). The value of the api.url property will be dynamically retrieved from the ApiServer bean.

  • Java

  • Kotlin

@Configuration
class TestConfig {

	@Bean
	@DynamicPropertySource
	ApiServer apiServer(DynamicPropertyRegistry registry) {
		ApiServer apiServer = new ApiServer();
		registry.add("api.url", apiServer::getUrl);
		return apiServer;
	}
}
@Configuration
class TestConfig {

	@Bean
	@DynamicPropertySource
	fun apiServer(registry: DynamicPropertyRegistry): ApiServer {
		val apiServer = ApiServer()
		registry.add("api.url", apiServer::getUrl)
		return apiServer
	}
}
The use of @DynamicPropertySource on the @Bean method is optional and results in the ApiServer bean being eagerly initialized so that other beans in the context can be given access to the dynamic properties sourced from the ApiServer bean when those other beans are initialized.

Precedence

Dynamic properties have higher precedence than those loaded from @TestPropertySource, the operating system’s environment, Java system properties, or property sources added by the application declaratively by using @PropertySource or programmatically. Thus, dynamic properties can be used to selectively override properties loaded via @TestPropertySource, system property sources, and application property sources.