Document precedence for @DynamicPropertySource
Closes gh-24837
This commit is contained in:
parent
b7eb983107
commit
a842434bff
|
@ -41,8 +41,17 @@ import java.lang.annotation.Target;
|
||||||
* is resolved. Typically, method references are used to supply values, as in the
|
* is resolved. Typically, method references are used to supply values, as in the
|
||||||
* following example.
|
* following example.
|
||||||
*
|
*
|
||||||
* <h3>Example</h3>
|
* <h3>Precedence</h3>
|
||||||
|
* <p>Dynamic properties have higher precedence than those loaded from
|
||||||
|
* {@link TestPropertySource @TestPropertySource}, the operating system's
|
||||||
|
* environment, Java system properties, or property sources added by the
|
||||||
|
* application declaratively by using
|
||||||
|
* {@link org.springframework.context.annotation.PropertySource @PropertySource}
|
||||||
|
* or programmatically. Thus, dynamic properties can be used to selectively
|
||||||
|
* override properties loaded via {@code @TestPropertySource}, system property
|
||||||
|
* sources, and application property sources.
|
||||||
*
|
*
|
||||||
|
* <h3>Example</h3>
|
||||||
* <pre class="code">
|
* <pre class="code">
|
||||||
* @SpringJUnitConfig(...)
|
* @SpringJUnitConfig(...)
|
||||||
* @Testcontainers
|
* @Testcontainers
|
||||||
|
|
|
@ -44,7 +44,9 @@ import org.springframework.core.annotation.AliasFor;
|
||||||
* or some other means). Thus, test property sources can be used to selectively
|
* or some other means). Thus, test property sources can be used to selectively
|
||||||
* override properties defined in system and application property sources.
|
* override properties defined in system and application property sources.
|
||||||
* Furthermore, inlined {@link #properties} have higher precedence than
|
* Furthermore, inlined {@link #properties} have higher precedence than
|
||||||
* properties loaded from resource {@link #locations}.
|
* properties loaded from resource {@link #locations}. Note, however, that
|
||||||
|
* properties registered via {@link DynamicPropertySource @DynamicPropertySource}
|
||||||
|
* have higher precedence than those loaded via {@code @TestPropertySource}.
|
||||||
*
|
*
|
||||||
* <h3>Default Properties File Detection</h3>
|
* <h3>Default Properties File Detection</h3>
|
||||||
* <p>If {@code @TestPropertySource} is declared as an <em>empty</em> annotation
|
* <p>If {@code @TestPropertySource} is declared as an <em>empty</em> annotation
|
||||||
|
|
|
@ -16,38 +16,71 @@
|
||||||
|
|
||||||
package org.springframework.test.context;
|
package org.springframework.test.context;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.AfterAll;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.TestInstance;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.core.env.ConfigurableEnvironment;
|
||||||
|
import org.springframework.core.env.MutablePropertySources;
|
||||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Integration test for {@link DynamicPropertySource @DynamicPropertySource}.
|
* Integration tests for {@link DynamicPropertySource @DynamicPropertySource}.
|
||||||
*
|
*
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
*/
|
*/
|
||||||
@SpringJUnitConfig
|
@SpringJUnitConfig
|
||||||
|
@TestPropertySource(properties = "test.container.ip: test")
|
||||||
|
@TestInstance(PER_CLASS)
|
||||||
|
@DisplayName("@DynamicPropertySource integration tests")
|
||||||
class DynamicPropertySourceIntegrationTests {
|
class DynamicPropertySourceIntegrationTests {
|
||||||
|
|
||||||
|
private static final String TEST_CONTAINER_IP = "test.container.ip";
|
||||||
|
|
||||||
|
static {
|
||||||
|
System.setProperty(TEST_CONTAINER_IP, "system");
|
||||||
|
}
|
||||||
|
|
||||||
static DemoContainer container = new DemoContainer();
|
static DemoContainer container = new DemoContainer();
|
||||||
|
|
||||||
|
|
||||||
@DynamicPropertySource
|
@DynamicPropertySource
|
||||||
static void containerProperties(DynamicPropertyRegistry registry) {
|
static void containerProperties(DynamicPropertyRegistry registry) {
|
||||||
registry.add("test.container.ip", container::getIpAddress);
|
registry.add(TEST_CONTAINER_IP, container::getIpAddress);
|
||||||
registry.add("test.container.port", container::getPort);
|
registry.add("test.container.port", container::getPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@AfterAll
|
||||||
|
void clearSystemProperty() {
|
||||||
|
System.clearProperty(TEST_CONTAINER_IP);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void hasInjectedValues(@Autowired Service service) {
|
@DisplayName("@DynamicPropertySource overrides @TestPropertySource and JVM system property")
|
||||||
|
void dynamicPropertySourceOverridesTestPropertySourceAndSystemProperty(@Autowired ConfigurableEnvironment env) {
|
||||||
|
MutablePropertySources propertySources = env.getPropertySources();
|
||||||
|
assertThat(propertySources.size()).isGreaterThanOrEqualTo(4);
|
||||||
|
assertThat(propertySources.contains("Dynamic Test Properties")).isTrue();
|
||||||
|
assertThat(propertySources.contains("Inlined Test Properties")).isTrue();
|
||||||
|
assertThat(propertySources.contains("systemProperties")).isTrue();
|
||||||
|
assertThat(propertySources.get("Dynamic Test Properties").getProperty(TEST_CONTAINER_IP)).isEqualTo("127.0.0.1");
|
||||||
|
assertThat(propertySources.get("Inlined Test Properties").getProperty(TEST_CONTAINER_IP)).isEqualTo("test");
|
||||||
|
assertThat(propertySources.get("systemProperties").getProperty(TEST_CONTAINER_IP)).isEqualTo("system");
|
||||||
|
assertThat(env.getProperty(TEST_CONTAINER_IP)).isEqualTo("127.0.0.1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("@Service has values injected from @DynamicPropertySource")
|
||||||
|
void serviceHasInjectedValues(@Autowired Service service) {
|
||||||
assertThat(service.getIp()).isEqualTo("127.0.0.1");
|
assertThat(service.getIp()).isEqualTo("127.0.0.1");
|
||||||
assertThat(service.getPort()).isEqualTo(4242);
|
assertThat(service.getPort()).isEqualTo(4242);
|
||||||
}
|
}
|
||||||
|
@ -58,7 +91,6 @@ class DynamicPropertySourceIntegrationTests {
|
||||||
static class Config {
|
static class Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component
|
|
||||||
static class Service {
|
static class Service {
|
||||||
|
|
||||||
private final String ip;
|
private final String ip;
|
||||||
|
|
|
@ -411,6 +411,7 @@ Spring's testing annotations include the following:
|
||||||
* <<spring-testing-annotation-contexthierarchy>>
|
* <<spring-testing-annotation-contexthierarchy>>
|
||||||
* <<spring-testing-annotation-activeprofiles>>
|
* <<spring-testing-annotation-activeprofiles>>
|
||||||
* <<spring-testing-annotation-testpropertysource>>
|
* <<spring-testing-annotation-testpropertysource>>
|
||||||
|
* <<spring-testing-annotation-dynamicpropertysource>>
|
||||||
* <<spring-testing-annotation-dirtiescontext>>
|
* <<spring-testing-annotation-dirtiescontext>>
|
||||||
* <<spring-testing-annotation-testexecutionlisteners>>
|
* <<spring-testing-annotation-testexecutionlisteners>>
|
||||||
* <<spring-testing-annotation-commit>>
|
* <<spring-testing-annotation-commit>>
|
||||||
|
@ -758,13 +759,6 @@ locations of properties files and inlined properties to be added to the set of
|
||||||
`PropertySources` in the `Environment` for an `ApplicationContext` loaded for an
|
`PropertySources` in the `Environment` for an `ApplicationContext` loaded for an
|
||||||
integration test.
|
integration test.
|
||||||
|
|
||||||
Test property sources have higher precedence than those loaded from the operating
|
|
||||||
system's environment or Java system properties as well as property sources added by the
|
|
||||||
application declaratively through `@PropertySource` or programmatically. Thus, test
|
|
||||||
property sources can be used to selectively override properties defined in system and
|
|
||||||
application property sources. Furthermore, inlined properties have higher precedence than
|
|
||||||
properties loaded from resource locations.
|
|
||||||
|
|
||||||
The following example demonstrates how to declare a properties file from the classpath:
|
The following example demonstrates how to declare a properties file from the classpath:
|
||||||
|
|
||||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
|
@ -816,6 +810,65 @@ The following example demonstrates how to declare inlined properties:
|
||||||
|
|
||||||
See <<testcontext-ctx-management-property-sources>> for examples and further details.
|
See <<testcontext-ctx-management-property-sources>> for examples and further details.
|
||||||
|
|
||||||
|
[[spring-testing-annotation-dynamicpropertysource]]
|
||||||
|
===== `@DynamicPropertySource`
|
||||||
|
|
||||||
|
`@DynamicPropertySource` is a method-level annotation that you can use to register
|
||||||
|
_dynamic_ properties to be added to the set of `PropertySources` in the `Environment` for
|
||||||
|
an `ApplicationContext` loaded for an integration test. Dynamic properties are useful
|
||||||
|
when you do not know the value of the properties upfront – for example, if the properties
|
||||||
|
are managed by an external resource such as for a container managed by the
|
||||||
|
https://www.testcontainers.org/[Testcontainers] project.
|
||||||
|
|
||||||
|
The following example demonstrates how to register a dynamic property:
|
||||||
|
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
|
.Java
|
||||||
|
----
|
||||||
|
@ContextConfiguration
|
||||||
|
class MyIntegrationTests {
|
||||||
|
|
||||||
|
static MyExternalServer server = // ...
|
||||||
|
|
||||||
|
@DynamicPropertySource // <1>
|
||||||
|
static void dynamicProperties(DynamicPropertyRegistry registry) { // <2>
|
||||||
|
registry.add("server.port", server::getPort); // <3>
|
||||||
|
}
|
||||||
|
|
||||||
|
// tests ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
<1> Annotate a `static` method with `@DynamicPropertySource`.
|
||||||
|
<2> Accept a `DynamicPropertyRegistry` as an argument.
|
||||||
|
<3> Register a dynamic `server.port` property to be retrieved lazily from the server.
|
||||||
|
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
.Kotlin
|
||||||
|
----
|
||||||
|
@ContextConfiguration
|
||||||
|
class MyIntegrationTests {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
val server: MyExternalServer = // ...
|
||||||
|
|
||||||
|
@DynamicPropertySource // <1>
|
||||||
|
@JvmStatic
|
||||||
|
fun dynamicProperties(registry: DynamicPropertyRegistry) { // <2>
|
||||||
|
registry.add("server.port", server::getPort) // <3>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// tests ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
<1> Annotate a `static` method with `@DynamicPropertySource`.
|
||||||
|
<2> Accept a `DynamicPropertyRegistry` as an argument.
|
||||||
|
<3> Register a dynamic `server.port` property to be retrieved lazily from the server.
|
||||||
|
|
||||||
|
See <<testcontext-ctx-management-dynamic-property-sources>> for further details.
|
||||||
|
|
||||||
[[spring-testing-annotation-dirtiescontext]]
|
[[spring-testing-annotation-dirtiescontext]]
|
||||||
===== `@DirtiesContext`
|
===== `@DirtiesContext`
|
||||||
|
|
||||||
|
@ -3835,12 +3888,14 @@ file is `classpath:com/example/MyTest.properties`. If the default cannot be dete
|
||||||
|
|
||||||
====== Precedence
|
====== Precedence
|
||||||
|
|
||||||
Test property sources have higher precedence than those loaded from the operating
|
Test properties have higher precedence than those defined in the operating system's
|
||||||
system's environment, Java system properties, or property sources added by the
|
environment, Java system properties, or property sources added by the application
|
||||||
application declaratively by using `@PropertySource` or programmatically. Thus, test
|
declaratively by using `@PropertySource` or programmatically. Thus, test properties can
|
||||||
property sources can be used to selectively override properties defined in system and
|
be used to selectively override properties loaded from system and application property
|
||||||
application property sources. Furthermore, inlined properties have higher precedence than
|
sources. Furthermore, inlined properties have higher precedence than properties loaded
|
||||||
properties loaded from resource locations.
|
from resource locations. Note, however, that properties registered via
|
||||||
|
<<testcontext-ctx-management-dynamic-property-sources, `@DynamicPropertySource`>> have
|
||||||
|
higher precedence than those loaded via `@TestPropertySource`.
|
||||||
|
|
||||||
In the next example, the `timezone` and `port` properties and any properties defined in
|
In the next example, the `timezone` and `port` properties and any properties defined in
|
||||||
`"/test.properties"` override any properties of the same name that are defined in system
|
`"/test.properties"` override any properties of the same name that are defined in system
|
||||||
|
@ -3968,8 +4023,8 @@ to define properties in both a subclass and its superclass by using inline prope
|
||||||
===== Context Configuration with Dynamic Property Sources
|
===== Context Configuration with Dynamic Property Sources
|
||||||
|
|
||||||
As of Spring Framework 5.2.5, the TestContext framework provides support for _dynamic_
|
As of Spring Framework 5.2.5, the TestContext framework provides support for _dynamic_
|
||||||
property sources via the `@DynamicPropertySource` annotation. This annotation can be used
|
properties via the `@DynamicPropertySource` annotation. This annotation can be used in
|
||||||
in integration tests that need to add properties with dynamic values to the set of
|
integration tests that need to add properties with dynamic values to the set of
|
||||||
`PropertySources` in the `Environment` for the `ApplicationContext` loaded for the
|
`PropertySources` in the `Environment` for the `ApplicationContext` loaded for the
|
||||||
integration test.
|
integration test.
|
||||||
|
|
||||||
|
@ -3988,8 +4043,9 @@ references are used to supply values, as can be seen in the following example wh
|
||||||
the Testcontainers project to manage a Redis container outside of the Spring
|
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
|
`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
|
available to components within the test's `ApplicationContext` via the `redis.host` and
|
||||||
`redis.port` properties. These properties can be injected into Spring-managed components
|
`redis.port` properties. These properties can be accessed via Spring's `Environment`
|
||||||
via `@Value("${redis.host}")` and `@Value("${redis.port}")`, respectively.
|
abstraction or injected directly into Spring-managed components – for example, via
|
||||||
|
`@Value("${redis.host}")` and `@Value("${redis.port}")`, respectively.
|
||||||
|
|
||||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
.Java
|
.Java
|
||||||
|
@ -4037,6 +4093,14 @@ via `@Value("${redis.host}")` and `@Value("${redis.port}")`, respectively.
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
|
====== 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.
|
||||||
|
|
||||||
[[testcontext-ctx-management-web]]
|
[[testcontext-ctx-management-web]]
|
||||||
===== Loading a `WebApplicationContext`
|
===== Loading a `WebApplicationContext`
|
||||||
|
|
||||||
|
@ -4272,7 +4336,9 @@ framework uses the following configuration parameters to build the context cache
|
||||||
* `locations` (from `@ContextConfiguration`)
|
* `locations` (from `@ContextConfiguration`)
|
||||||
* `classes` (from `@ContextConfiguration`)
|
* `classes` (from `@ContextConfiguration`)
|
||||||
* `contextInitializerClasses` (from `@ContextConfiguration`)
|
* `contextInitializerClasses` (from `@ContextConfiguration`)
|
||||||
* `contextCustomizers` (from `ContextCustomizerFactory`)
|
* `contextCustomizers` (from `ContextCustomizerFactory`) – this includes
|
||||||
|
`@DynamicPropertySource` methods as well as various features from Spring Boot's
|
||||||
|
testing support such as `@MockBean` and `@SpyBean`.
|
||||||
* `contextLoader` (from `@ContextConfiguration`)
|
* `contextLoader` (from `@ContextConfiguration`)
|
||||||
* `parent` (from `@ContextHierarchy`)
|
* `parent` (from `@ContextHierarchy`)
|
||||||
* `activeProfiles` (from `@ActiveProfiles`)
|
* `activeProfiles` (from `@ActiveProfiles`)
|
||||||
|
|
Loading…
Reference in New Issue