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
|
||||
* 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">
|
||||
* @SpringJUnitConfig(...)
|
||||
* @Testcontainers
|
||||
|
|
|
@ -44,7 +44,9 @@ import org.springframework.core.annotation.AliasFor;
|
|||
* or some other means). Thus, test property sources can be used to selectively
|
||||
* override properties defined in system and application property sources.
|
||||
* 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>
|
||||
* <p>If {@code @TestPropertySource} is declared as an <em>empty</em> annotation
|
||||
|
|
|
@ -16,38 +16,71 @@
|
|||
|
||||
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.TestInstance;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
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 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 Sam Brannen
|
||||
*/
|
||||
@SpringJUnitConfig
|
||||
@TestPropertySource(properties = "test.container.ip: test")
|
||||
@TestInstance(PER_CLASS)
|
||||
@DisplayName("@DynamicPropertySource integration tests")
|
||||
class DynamicPropertySourceIntegrationTests {
|
||||
|
||||
private static final String TEST_CONTAINER_IP = "test.container.ip";
|
||||
|
||||
static {
|
||||
System.setProperty(TEST_CONTAINER_IP, "system");
|
||||
}
|
||||
|
||||
static DemoContainer container = new DemoContainer();
|
||||
|
||||
|
||||
@DynamicPropertySource
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@AfterAll
|
||||
void clearSystemProperty() {
|
||||
System.clearProperty(TEST_CONTAINER_IP);
|
||||
}
|
||||
|
||||
@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.getPort()).isEqualTo(4242);
|
||||
}
|
||||
|
@ -58,7 +91,6 @@ class DynamicPropertySourceIntegrationTests {
|
|||
static class Config {
|
||||
}
|
||||
|
||||
@Component
|
||||
static class Service {
|
||||
|
||||
private final String ip;
|
||||
|
|
|
@ -411,6 +411,7 @@ Spring's testing annotations include the following:
|
|||
* <<spring-testing-annotation-contexthierarchy>>
|
||||
* <<spring-testing-annotation-activeprofiles>>
|
||||
* <<spring-testing-annotation-testpropertysource>>
|
||||
* <<spring-testing-annotation-dynamicpropertysource>>
|
||||
* <<spring-testing-annotation-dirtiescontext>>
|
||||
* <<spring-testing-annotation-testexecutionlisteners>>
|
||||
* <<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
|
||||
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:
|
||||
|
||||
[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.
|
||||
|
||||
[[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]]
|
||||
===== `@DirtiesContext`
|
||||
|
||||
|
@ -3835,12 +3888,14 @@ file is `classpath:com/example/MyTest.properties`. If the default cannot be dete
|
|||
|
||||
====== Precedence
|
||||
|
||||
Test property sources have higher precedence than those loaded from the operating
|
||||
system's environment, Java system properties, or property sources added by the
|
||||
application declaratively by using `@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.
|
||||
Test properties have higher precedence than those defined in the operating system's
|
||||
environment, Java system properties, or property sources added by the application
|
||||
declaratively by using `@PropertySource` or programmatically. Thus, test properties can
|
||||
be used to selectively override properties loaded from system and application property
|
||||
sources. Furthermore, inlined properties have higher precedence than properties loaded
|
||||
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
|
||||
`"/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
|
||||
|
||||
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
|
||||
in integration tests that need to add properties with dynamic values to the set of
|
||||
properties via the `@DynamicPropertySource` annotation. This annotation can be used in
|
||||
integration tests that need to add properties with dynamic values to the set of
|
||||
`PropertySources` in the `Environment` for the `ApplicationContext` loaded for the
|
||||
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
|
||||
`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 injected into Spring-managed components
|
||||
via `@Value("${redis.host}")` and `@Value("${redis.port}")`, respectively.
|
||||
`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.
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.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]]
|
||||
===== Loading a `WebApplicationContext`
|
||||
|
||||
|
@ -4272,7 +4336,9 @@ framework uses the following configuration parameters to build the context cache
|
|||
* `locations` (from `@ContextConfiguration`)
|
||||
* `classes` (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`)
|
||||
* `parent` (from `@ContextHierarchy`)
|
||||
* `activeProfiles` (from `@ActiveProfiles`)
|
||||
|
|
Loading…
Reference in New Issue