|
1 | 1 | [[testcontext-ctx-management-dynamic-property-sources]]
|
2 | 2 | = Context Configuration with Dynamic Property Sources
|
3 | 3 |
|
4 |
| -As of Spring Framework 5.2.5, the TestContext framework provides support for _dynamic_ |
5 |
| -properties via the `@DynamicPropertySource` annotation. This annotation can be used in |
6 |
| -integration tests that need to add properties with dynamic values to the set of |
7 |
| -`PropertySources` in the `Environment` for the `ApplicationContext` loaded for the |
8 |
| -integration test. |
| 4 | +The Spring TestContext Framework provides support for _dynamic_ properties via the |
| 5 | +`@DynamicPropertySource` annotation and the `DynamicPropertyRegistry`. |
9 | 6 |
|
10 | 7 | [NOTE]
|
11 | 8 | ====
|
12 |
| -The `@DynamicPropertySource` annotation and its supporting infrastructure were |
13 |
| -originally designed to allow properties from |
14 |
| -{testcontainers-site}[Testcontainers] based tests to be exposed easily to |
15 |
| -Spring integration tests. However, this feature may also be used with any form of |
16 |
| -external resource whose lifecycle is maintained outside the test's `ApplicationContext`. |
| 9 | +The `@DynamicPropertySource` annotation and its supporting infrastructure were originally |
| 10 | +designed to allow properties from {testcontainers-site}[Testcontainers] based tests to be |
| 11 | +exposed easily to Spring integration tests. However, this feature may be used with any |
| 12 | +form of external resource whose lifecycle is managed outside the test's |
| 13 | +`ApplicationContext` or with beans whose lifecycle is managed by the test's |
| 14 | +`ApplicationContext`. |
17 | 15 | ====
|
18 | 16 |
|
19 |
| -In contrast to the xref:testing/testcontext-framework/ctx-management/property-sources.adoc[`@TestPropertySource`] |
20 |
| -annotation that is applied at the class level, `@DynamicPropertySource` must be applied |
21 |
| -to a `static` method that accepts a single `DynamicPropertyRegistry` argument which is |
22 |
| -used to add _name-value_ pairs to the `Environment`. Values are dynamic and provided via |
23 |
| -a `Supplier` which is only invoked when the property is resolved. Typically, method |
24 |
| -references are used to supply values, as can be seen in the following example which uses |
25 |
| -the Testcontainers project to manage a Redis container outside of the Spring |
26 |
| -`ApplicationContext`. The IP address and port of the managed Redis container are made |
27 |
| -available to components within the test's `ApplicationContext` via the `redis.host` and |
28 |
| -`redis.port` properties. These properties can be accessed via Spring's `Environment` |
29 |
| -abstraction or injected directly into Spring-managed components – for example, via |
30 |
| -`@Value("${redis.host}")` and `@Value("${redis.port}")`, respectively. |
| 17 | +In contrast to the |
| 18 | +xref:testing/testcontext-framework/ctx-management/property-sources.adoc[`@TestPropertySource`] |
| 19 | +annotation that is applied at the class level, `@DynamicPropertySource` can be applied to |
| 20 | +`static` methods in integration test classes or to `@Bean` methods in test |
| 21 | +`@Configuration` classes in order to add properties with dynamic values to the set of |
| 22 | +`PropertySources` in the `Environment` for the `ApplicationContext` loaded for the |
| 23 | +integration test. |
| 24 | + |
| 25 | +A `DynamicPropertyRegistry` is used to add _name-value_ pairs to the `Environment`. |
| 26 | +Values are dynamic and provided via a `Supplier` which is only invoked when the property |
| 27 | +is resolved. Typically, method references are used to supply values. |
| 28 | + |
| 29 | +Methods in integration test classes that are annotated with `@DynamicPropertySource` must |
| 30 | +be `static` and must accept a single `DynamicPropertyRegistry` argument. |
| 31 | + |
| 32 | +`@Bean` methods annotated with `@DynamicPropertySource` may either accept an argument of |
| 33 | +type `DynamicPropertyRegistry` or access a `DynamicPropertyRegistry` instance autowired |
| 34 | +into their enclosing `@Configuration` class. Note, however, that `@Bean` methods which |
| 35 | +interact with a `DynamicPropertyRegistry` are not required to be annotated with |
| 36 | +`@DynamicPropertySource` unless they need to enforce eager initialization of the bean |
| 37 | +within the context. See the class-level javadoc for `DynamicPropertyRegistry` for details. |
31 | 38 |
|
32 | 39 | [TIP]
|
33 | 40 | ====
|
34 | 41 | If you use `@DynamicPropertySource` in a base class and discover that tests in subclasses
|
35 | 42 | fail because the dynamic properties change between subclasses, you may need to annotate
|
36 |
| -your base class with xref:testing/annotations/integration-spring/annotation-dirtiescontext.adoc[`@DirtiesContext`] to |
37 |
| -ensure that each subclass gets its own `ApplicationContext` with the correct dynamic |
| 43 | +your base class with |
| 44 | +xref:testing/annotations/integration-spring/annotation-dirtiescontext.adoc[`@DirtiesContext`] |
| 45 | +to ensure that each subclass gets its own `ApplicationContext` with the correct dynamic |
38 | 46 | properties.
|
39 | 47 | ====
|
40 | 48 |
|
| 49 | +The following example uses the Testcontainers project to manage a Redis container outside |
| 50 | +of the Spring `ApplicationContext`. The IP address and port of the managed Redis |
| 51 | +container are made available to components within the test's `ApplicationContext` via the |
| 52 | +`redis.host` and `redis.port` properties. These properties can be accessed via Spring's |
| 53 | +`Environment` abstraction or injected directly into Spring-managed components – for |
| 54 | +example, via `@Value("${redis.host}")` and `@Value("${redis.port}")`, respectively. |
| 55 | + |
41 | 56 | [tabs]
|
42 | 57 | ======
|
43 | 58 | Java::
|
@@ -92,7 +107,55 @@ Kotlin::
|
92 | 107 | ----
|
93 | 108 | ======
|
94 | 109 |
|
95 |
| -[[precedence]] |
| 110 | +The following example demonstrates how to use `DynamicPropertyRegistry` and |
| 111 | +`@DynamicPropertySource` with a `@Bean` method. The `api.url` property can be accessed |
| 112 | +via Spring's `Environment` abstraction or injected directly into other Spring-managed |
| 113 | +components – for example, via `@Value("${api.url}")`. The value of the `api.url` property |
| 114 | +will be dynamically retrieved from the `ApiServer` bean. |
| 115 | + |
| 116 | +[tabs] |
| 117 | +====== |
| 118 | +Java:: |
| 119 | ++ |
| 120 | +[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
| 121 | +---- |
| 122 | + @Configuration |
| 123 | + class TestConfig { |
| 124 | +
|
| 125 | + @Bean |
| 126 | + @DynamicPropertySource |
| 127 | + ApiServer apiServer(DynamicPropertyRegistry registry) { |
| 128 | + ApiServer apiServer = new ApiServer(); |
| 129 | + registry.add("api.url", apiServer::getUrl); |
| 130 | + return apiServer; |
| 131 | + } |
| 132 | + } |
| 133 | +---- |
| 134 | +
|
| 135 | +Kotlin:: |
| 136 | ++ |
| 137 | +[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
| 138 | +---- |
| 139 | + @Configuration |
| 140 | + class TestConfig { |
| 141 | +
|
| 142 | + @Bean |
| 143 | + @DynamicPropertySource |
| 144 | + fun apiServer(registry: DynamicPropertyRegistry): ApiServer { |
| 145 | + val apiServer = ApiServer() |
| 146 | + registry.add("api.url", apiServer::getUrl) |
| 147 | + return apiServer |
| 148 | + } |
| 149 | + } |
| 150 | +---- |
| 151 | +====== |
| 152 | + |
| 153 | +NOTE: The use of `@DynamicPropertySource` on the `@Bean` method is optional and results |
| 154 | +in the `ApiServer` bean being eagerly initialized so that other beans in the context can |
| 155 | +be given access to the dynamic properties sourced from the `ApiServer` bean when those |
| 156 | +other beans are initialized. |
| 157 | + |
| 158 | +[[testcontext-ctx-management-dynamic-property-sources-precedence]] |
96 | 159 | == Precedence
|
97 | 160 |
|
98 | 161 | Dynamic properties have higher precedence than those loaded from `@TestPropertySource`,
|
|
0 commit comments