Browse Source

Document precedence for @DynamicPropertySource

Closes gh-24837
pull/24841/head
Sam Brannen 5 years ago
parent
commit
a842434bff
  1. 11
      spring-test/src/main/java/org/springframework/test/context/DynamicPropertySource.java
  2. 4
      spring-test/src/main/java/org/springframework/test/context/TestPropertySource.java
  3. 44
      spring-test/src/test/java/org/springframework/test/context/DynamicPropertySourceIntegrationTests.java
  4. 102
      src/docs/asciidoc/testing.adoc

11
spring-test/src/main/java/org/springframework/test/context/DynamicPropertySource.java

@ -41,8 +41,17 @@ import java.lang.annotation.Target; @@ -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">
* &#064;SpringJUnitConfig(...)
* &#064;Testcontainers

4
spring-test/src/main/java/org/springframework/test/context/TestPropertySource.java

@ -44,7 +44,9 @@ import org.springframework.core.annotation.AliasFor; @@ -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

44
spring-test/src/test/java/org/springframework/test/context/DynamicPropertySourceIntegrationTests.java

@ -16,38 +16,71 @@ @@ -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 {
static DemoContainer container = new DemoContainer();
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
@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
void hasInjectedValues(@Autowired Service service) {
@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 { @@ -58,7 +91,6 @@ class DynamicPropertySourceIntegrationTests {
static class Config {
}
@Component
static class Service {
private final String ip;

102
src/docs/asciidoc/testing.adoc

@ -411,6 +411,7 @@ Spring's testing annotations include the following: @@ -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 @@ -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: @@ -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 @@ -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 @@ -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 @@ -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. @@ -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 @@ -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…
Cancel
Save