Introduce DynamicPropertyRegistrar to replace DynamicPropertyRegistry bean

Spring Boot's testing support registers a DynamicPropertyRegistry as a
bean in the ApplicationContext, which conflicts with the
DynamicPropertyRegistry registered as a bean by the Spring TestContext
Framework (TCF) since Spring Framework 6.2 M2.

To avoid that conflict and to improve the user experience for Spring's
testing support, this commit introduces a DynamicPropertyRegistrar API
to replace the DynamicPropertyRegistry bean support.

Specifically, the TCF no longer registers a DynamicPropertyRegistry as
a bean in the ApplicationContext.

Instead, users can now register custom implementations of
DynamicPropertyRegistrar as beans in the ApplicationContext, and the
DynamicPropertiesContextCustomizer now registers a
DynamicPropertyRegistrarBeanInitializer which eagerly initializes
DynamicPropertyRegistrar beans and invokes their accept() methods with
an appropriate DynamicPropertyRegistry.

In addition, a singleton DynamicValuesPropertySource is created and
registered with the Environment for use in
DynamicPropertiesContextCustomizer and
DynamicPropertyRegistrarBeanInitializer, which allows
@⁠DynamicPropertySource methods and DynamicPropertyRegistrar beans to
transparently populate the same DynamicValuesPropertySource.

Closes gh-33501
This commit is contained in:
Sam Brannen 2024-09-10 16:34:50 +02:00
parent 78028cde05
commit e7b52cf8b2
15 changed files with 429 additions and 301 deletions

View File

@ -2,39 +2,51 @@
= Context Configuration with Dynamic Property Sources
The Spring TestContext Framework provides support for _dynamic_ properties via the
`@DynamicPropertySource` annotation and the `DynamicPropertyRegistry`.
`DynamicPropertyRegistry`, the `@DynamicPropertySource` annotation, and the
`DynamicPropertyRegistrar` API.
[NOTE]
====
The `@DynamicPropertySource` annotation and its supporting infrastructure were originally
designed to allow properties from {testcontainers-site}[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`.
The dynamic property source infrastructure was originally designed to allow properties
from {testcontainers-site}[Testcontainers] based tests to be exposed easily to Spring
integration tests. However, these features 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`.
====
[[testcontext-ctx-management-dynamic-property-sources-precedence]]
== 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-dynamic-property-sources-dynamic-property-registry]]
== `DynamicPropertyRegistry`
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. The following
sections provide examples of how to use the `DynamicPropertyRegistry`.
[[testcontext-ctx-management-dynamic-property-sources-dynamic-property-source]]
== `@DynamicPropertySource`
In contrast to the
xref:testing/testcontext-framework/ctx-management/property-sources.adoc[`@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.
`static` methods in integration test 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.
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.
be `static` and must accept a single `DynamicPropertyRegistry` argument. See the
class-level javadoc for `DynamicPropertyRegistry` for further details.
[TIP]
====
@ -107,11 +119,33 @@ Kotlin::
----
======
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.
[[testcontext-ctx-management-dynamic-property-sources-dynamic-property-registrar]]
== `DynamicPropertyRegistrar`
As an alternative to implementing `@DynamicPropertySource` methods in integration test
classes, you can register implementations of the `DynamicPropertyRegistrar` API as beans
within the test's `ApplicationContext`. Doing so allows you to support additional use
cases that are not possible with a `@DynamicPropertySource` method. For example, since a
`DynamicPropertyRegistrar` is itself a bean in the `ApplicationContext`, it can interact
with other beans in the context and register dynamic properties that are sourced from
those beans.
Any bean in a test's `ApplicationContext` that implements the `DynamicPropertyRegistrar`
interface will be automatically detected and eagerly initialized before the singleton
pre-instantiation phase, and the `accept()` methods of such beans will be invoked with a
`DynamicPropertyRegistry` that performs the actual dynamic property registration on
behalf of the registrar.
WARNING: Any interaction with other beans results in eager initialization of those other
beans and their dependencies.
The following example demonstrates how to implement a `DynamicPropertyRegistrar` as a
lambda expression that registers a dynamic property for the `ApiServer` bean. 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}")`,
and the value of the `api.url` property will be dynamically retrieved from the
`ApiServer` bean.
[tabs]
======
@ -123,11 +157,13 @@ Java::
class TestConfig {
@Bean
@DynamicPropertySource
ApiServer apiServer(DynamicPropertyRegistry registry) {
ApiServer apiServer = new ApiServer();
registry.add("api.url", apiServer::getUrl);
return apiServer;
ApiServer apiServer() {
return new ApiServer();
}
@Bean
DynamicPropertyRegistrar apiServerProperties(ApiServer apiServer) {
return registry -> registry.add("api.url", apiServer::getUrl);
}
}
----
@ -140,27 +176,14 @@ Kotlin::
class TestConfig {
@Bean
@DynamicPropertySource
fun apiServer(registry: DynamicPropertyRegistry): ApiServer {
val apiServer = ApiServer()
registry.add("api.url", apiServer::getUrl)
return apiServer
fun apiServer(): ApiServer {
return ApiServer()
}
@Bean
fun apiServerProperties(apiServer: ApiServer): DynamicPropertyRegistrar {
return registry -> registry.add("api.url", apiServer::getUrl)
}
}
----
======
NOTE: 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.
[[testcontext-ctx-management-dynamic-property-sources-precedence]]
== 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.

View File

@ -0,0 +1,87 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context;
/**
* Registrar that is used to add properties with dynamically resolved values to
* the {@code Environment} via a {@link DynamicPropertyRegistry}.
*
* <p>Any bean in a test's {@code ApplicationContext} that implements the
* {@code DynamicPropertyRegistrar} interface will be automatically detected and
* eagerly initialized before the singleton pre-instantiation phase, and the
* {@link #accept} methods of such beans will be invoked with a
* {@code DynamicPropertyRegistry} that performs the actual dynamic property
* registration on behalf of the registrar.
*
* <p>This is an alternative to implementing
* {@link DynamicPropertySource @DynamicPropertySource} methods in integration
* test classes and supports additional use cases that are not possible with a
* {@code @DynamicPropertySource} method. For example, since a
* {@code DynamicPropertyRegistrar} is itself a bean in the {@code ApplicationContext},
* it can interact with other beans in the context and register dynamic properties
* that are sourced from those beans. Note, however, that any interaction with
* other beans results in eager initialization of those other beans and their
* dependencies.
*
* <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>
*
* <p>The following example demonstrates how to implement a
* {@code DynamicPropertyRegistrar} as a lambda expression that registers a
* dynamic property for the {@code ApiServer} bean. Other beans in the
* {@code ApplicationContext} can access the {@code api.url} property which is
* dynamically retrieved from the {@code ApiServer} bean &mdash; for example,
* via {@code @Value("${api.url}")}.
*
* <pre class="code">
* &#064;Configuration
* class TestConfig {
*
* &#064;Bean
* ApiServer apiServer() {
* return new ApiServer();
* }
*
* &#064;Bean
* DynamicPropertyRegistrar apiServerProperties(ApiServer apiServer) {
* return registry -> registry.add("api.url", apiServer::getUrl);
* }
*
* }</pre>
*
* @author Sam Brannen
* @since 6.2
* @see DynamicPropertySource
* @see DynamicPropertyRegistry
* @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory#preInstantiateSingletons()
*/
@FunctionalInterface
public interface DynamicPropertyRegistrar {
void accept(DynamicPropertyRegistry registry);
}

View File

@ -27,22 +27,18 @@ import java.util.function.Supplier;
* test classes.
*
* <p>As of Spring Framework 6.2, a {@code DynamicPropertyRegistry} is also
* registered as a singleton bean in the test's {@code ApplicationContext}. This
* allows a {@code DynamicPropertyRegistry} to be autowired into a
* {@code @Configuration} class or supplied to a {@code @Bean} method as an
* argument, making it possible to register a dynamic property from within a test's
* {@code ApplicationContext}. For example, a {@code @Bean} method can register
* a property whose value is dynamically sourced from the bean that the method
* returns. Note that such a {@code @Bean} method can optionally be annotated
* with {@code @DynamicPropertySource} to enforce eager initialization of the
* bean within the context, thereby ensuring that any dynamic properties sourced
* from that bean are available to other singleton beans within the context.
* See {@link DynamicPropertySource @DynamicPropertySource} for an example.
* supplied to {@link DynamicPropertyRegistrar} beans in the test's
* {@code ApplicationContext}, making it possible to register dynamic properties
* based on beans in the context. For example, a {@code @Bean} method can return
* a {@code DynamicPropertyRegistrar} that registers a property whose value is
* dynamically sourced from another bean in the context. See the documentation
* for {@code DynamicPropertyRegistrar} for an example.
*
* @author Phillip Webb
* @author Sam Brannen
* @since 5.2.5
* @see DynamicPropertySource
* @see DynamicPropertyRegistrar
*/
public interface DynamicPropertyRegistry {

View File

@ -24,42 +24,35 @@ import java.lang.annotation.Target;
/**
* {@code @DynamicPropertySource} is an annotation that can be applied to static
* methods in integration test classes or to {@code @Bean} methods in test
* {@code @Configuration} classes in order to add properties with dynamic values
* to the {@code Environment}'s set of {@code PropertySources}.
* methods in integration test classes in order to add properties with dynamic
* values to the {@code Environment}'s set of {@code PropertySources}.
*
* <p>Alternatively, dynamic properties can be added to the {@code Environment}
* by special beans in the test's {@code ApplicationContext}. See
* {@link DynamicPropertyRegistrar} for details.
*
* <p>This annotation and its supporting infrastructure were originally designed
* to allow properties from
* <a href="https://www.testcontainers.org/">Testcontainers</a> 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 {@code ApplicationContext} or with beans whose lifecycle is managed by
* the test's {@code ApplicationContext}.
* test's {@code ApplicationContext}.
*
* <p>{@code @DynamicPropertySource}-annotated methods use a
* {@code DynamicPropertyRegistry} to add <em>name-value</em> pairs to the
* {@code Environment}'s set of {@code PropertySources}. Values are dynamic and
* provided via a {@link java.util.function.Supplier} which is only invoked when
* the property is resolved. Typically, method references are used to supply values,
* as in the example below.
* <p>{@code @DynamicPropertySource} methods use a {@link DynamicPropertyRegistry}
* to add <em>name-value</em> pairs to the {@code Environment}'s set of
* {@code PropertySources}. Values are dynamic and provided via a
* {@link java.util.function.Supplier} which is only invoked when the property is
* resolved. Typically, method references are used to supply values, as in the
* example below.
*
* <p>Methods in integration test classes that are annotated with
* {@code @DynamicPropertySource} must be {@code static} and must accept a single
* {@link DynamicPropertyRegistry} argument.
*
* <p>{@code @Bean} methods annotated with {@code @DynamicPropertySource} may
* either accept an argument of type {@code DynamicPropertyRegistry} or access a
* {@code DynamicPropertyRegistry} instance autowired into their enclosing
* {@code @Configuration} class. Note, however, that {@code @Bean} methods which
* interact with a {@code DynamicPropertyRegistry} are not required to be annotated
* with {@code @DynamicPropertySource} unless they need to enforce eager
* initialization of the bean within the context.
* See {@link DynamicPropertyRegistry} for details.
* {@code DynamicPropertyRegistry} argument.
*
* <p>Dynamic properties from methods annotated with {@code @DynamicPropertySource}
* will be <em>inherited</em> from enclosing test classes, analogous to inheritance
* from superclasses and interfaces.
* See {@link NestedTestConfiguration @NestedTestConfiguration} for details.
* from superclasses and interfaces. See
* {@link NestedTestConfiguration @NestedTestConfiguration} for details.
*
* <p><strong>NOTE</strong>: if you use {@code @DynamicPropertySource} in a base
* class and discover that tests in subclasses fail because the dynamic properties
@ -69,6 +62,7 @@ import java.lang.annotation.Target;
* correct dynamic properties.
*
* <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
@ -103,28 +97,11 @@ import java.lang.annotation.Target;
* }
* }</pre>
*
* <p>The following example demonstrates how to use {@code @DynamicPropertySource}
* with a {@code @Bean} method. Beans in the {@code ApplicationContext} can
* access the {@code api.url} property which is dynamically retrieved from the
* {@code ApiServer} bean.
*
* <pre class="code">
* &#064;Configuration
* class TestConfig {
*
* &#064;Bean
* &#064;DynamicPropertySource
* ApiServer apiServer(DynamicPropertyRegistry registry) {
* ApiServer apiServer = new ApiServer();
* registry.add("api.url", apiServer::getUrl);
* return apiServer;
* }
* }</pre>
*
* @author Phillip Webb
* @author Sam Brannen
* @since 5.2.5
* @see DynamicPropertyRegistry
* @see DynamicPropertyRegistrar
* @see ContextConfiguration
* @see TestPropertySource
* @see org.springframework.core.env.PropertySource

View File

@ -1,79 +0,0 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.support;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.util.Assert;
/**
* Default {@link DynamicPropertyRegistry} implementation.
*
* @author Sam Brannen
* @since 6.2
*/
final class DefaultDynamicPropertyRegistry implements DynamicPropertyRegistry {
final Map<String, Supplier<Object>> valueSuppliers = Collections.synchronizedMap(new LinkedHashMap<>());
private final ConfigurableEnvironment environment;
private final boolean lazilyRegisterPropertySource;
private final Lock propertySourcesLock = new ReentrantLock();
DefaultDynamicPropertyRegistry(ConfigurableEnvironment environment, boolean lazilyRegisterPropertySource) {
this.environment = environment;
this.lazilyRegisterPropertySource = lazilyRegisterPropertySource;
}
@Override
public void add(String name, Supplier<Object> valueSupplier) {
Assert.hasText(name, "'name' must not be null or blank");
Assert.notNull(valueSupplier, "'valueSupplier' must not be null");
if (this.lazilyRegisterPropertySource) {
ensurePropertySourceIsRegistered();
}
this.valueSuppliers.put(name, valueSupplier);
}
private void ensurePropertySourceIsRegistered() {
MutablePropertySources propertySources = this.environment.getPropertySources();
this.propertySourcesLock.lock();
try {
PropertySource<?> ps = propertySources.get(DynamicValuesPropertySource.PROPERTY_SOURCE_NAME);
if (ps == null) {
propertySources.addFirst(new DynamicValuesPropertySource(this.valueSuppliers));
}
}
finally {
this.propertySourcesLock.unlock();
}
}
}

View File

@ -26,35 +26,32 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.lang.Nullable;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
/**
* {@link ContextCustomizer} which supports
* {@link DynamicPropertySource @DynamicPropertySource} methods and registers a
* {@link DynamicPropertyRegistry} as a singleton bean in the container for use
* in {@code @Configuration} classes and {@code @Bean} methods.
* {@link org.springframework.test.context.DynamicPropertySource @DynamicPropertySource}
* methods in test classes and registers a {@link DynamicPropertyRegistrarBeanInitializer}
* in the container to eagerly initialize
* {@link org.springframework.test.context.DynamicPropertyRegistrar DynamicPropertyRegistrar}
* beans.
*
* @author Phillip Webb
* @author Sam Brannen
* @since 5.2.5
* @see DynamicPropertiesContextCustomizerFactory
* @see DefaultDynamicPropertyRegistry
* @see DynamicPropertySourceBeanInitializer
* @see DynamicPropertyRegistrarBeanInitializer
*/
class DynamicPropertiesContextCustomizer implements ContextCustomizer {
private static final String DYNAMIC_PROPERTY_REGISTRY_BEAN_NAME =
DynamicPropertiesContextCustomizer.class.getName() + ".dynamicPropertyRegistry";
private static final String DYNAMIC_PROPERTY_SOURCE_BEAN_INITIALIZER_BEAN_NAME =
DynamicPropertiesContextCustomizer.class.getName() + ".dynamicPropertySourceBeanInitializer";
private static final String DYNAMIC_PROPERTY_REGISTRAR_BEAN_INITIALIZER_BEAN_NAME =
DynamicPropertiesContextCustomizer.class.getName() + ".dynamicPropertyRegistrarBeanInitializer";
private final Set<Method> methods;
@ -68,29 +65,25 @@ class DynamicPropertiesContextCustomizer implements ContextCustomizer {
@Override
public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
ConfigurableEnvironment environment = context.getEnvironment();
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
if (!(beanFactory instanceof BeanDefinitionRegistry beanDefinitionRegistry)) {
throw new IllegalStateException("BeanFactory must be a BeanDefinitionRegistry");
}
DefaultDynamicPropertyRegistry dynamicPropertyRegistry =
new DefaultDynamicPropertyRegistry(environment, this.methods.isEmpty());
beanFactory.registerSingleton(DYNAMIC_PROPERTY_REGISTRY_BEAN_NAME, dynamicPropertyRegistry);
if (!beanDefinitionRegistry.containsBeanDefinition(DYNAMIC_PROPERTY_SOURCE_BEAN_INITIALIZER_BEAN_NAME)) {
BeanDefinition beanDefinition = new RootBeanDefinition(DynamicPropertySourceBeanInitializer.class);
if (!beanDefinitionRegistry.containsBeanDefinition(DYNAMIC_PROPERTY_REGISTRAR_BEAN_INITIALIZER_BEAN_NAME)) {
BeanDefinition beanDefinition = new RootBeanDefinition(DynamicPropertyRegistrarBeanInitializer.class);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
beanDefinitionRegistry.registerBeanDefinition(
DYNAMIC_PROPERTY_SOURCE_BEAN_INITIALIZER_BEAN_NAME, beanDefinition);
DYNAMIC_PROPERTY_REGISTRAR_BEAN_INITIALIZER_BEAN_NAME, beanDefinition);
}
if (!this.methods.isEmpty()) {
MutablePropertySources propertySources = environment.getPropertySources();
propertySources.addFirst(new DynamicValuesPropertySource(dynamicPropertyRegistry.valueSuppliers));
ConfigurableEnvironment environment = context.getEnvironment();
DynamicValuesPropertySource propertySource = DynamicValuesPropertySource.getOrCreate(environment);
DynamicPropertyRegistry registry = propertySource.dynamicPropertyRegistry;
this.methods.forEach(method -> {
ReflectionUtils.makeAccessible(method);
ReflectionUtils.invokeMethod(method, null, dynamicPropertyRegistry);
ReflectionUtils.invokeMethod(method, null, registry);
});
}
}

View File

@ -27,15 +27,14 @@ import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.lang.Nullable;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.ContextCustomizerFactory;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestContextAnnotationUtils;
/**
* {@link ContextCustomizerFactory} which supports
* {@link DynamicPropertySource @DynamicPropertySource} methods and the
* registration of a {@link DynamicPropertyRegistry} as a singleton bean in the
* container for use in {@code @Configuration} classes and {@code @Bean} methods.
* {@link DynamicPropertySource @DynamicPropertySource} methods in test classes
* and {@link org.springframework.test.context.DynamicPropertyRegistrar
* DynamicPropertyRegistrar} beans in the container.
*
* @author Phillip Webb
* @author Sam Brannen

View File

@ -0,0 +1,76 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.support;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactoryInitializer;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.lang.Nullable;
import org.springframework.test.context.DynamicPropertyRegistrar;
import org.springframework.test.context.DynamicPropertyRegistry;
/**
* Internal component which eagerly initializes {@link DynamicPropertyRegistrar}
* beans.
*
* @author Sam Brannen
* @since 6.2
*/
class DynamicPropertyRegistrarBeanInitializer implements BeanFactoryInitializer<ListableBeanFactory>, EnvironmentAware {
private static final Log logger = LogFactory.getLog(DynamicPropertyRegistrarBeanInitializer.class);
@Nullable
private ConfigurableEnvironment environment;
@Override
public void setEnvironment(Environment environment) {
if (!(environment instanceof ConfigurableEnvironment configurableEnvironment)) {
throw new IllegalArgumentException("Environment must be a ConfigurableEnvironment");
}
this.environment = configurableEnvironment;
}
@Override
public void initialize(ListableBeanFactory beanFactory) {
if (this.environment == null) {
throw new IllegalStateException("Environment is required");
}
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
beanFactory, DynamicPropertyRegistrar.class);
if (beanNames.length > 0) {
DynamicValuesPropertySource propertySource = DynamicValuesPropertySource.getOrCreate(this.environment);
DynamicPropertyRegistry registry = propertySource.dynamicPropertyRegistry;
for (String name : beanNames) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly initializing DynamicPropertyRegistrar bean '%s'".formatted(name));
}
DynamicPropertyRegistrar registrar = beanFactory.getBean(name, DynamicPropertyRegistrar.class);
registrar.accept(registry);
}
}
}
}

View File

@ -1,48 +0,0 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.support;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactoryInitializer;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.test.context.DynamicPropertySource;
/**
* Internal component which eagerly initializes beans created by {@code @Bean}
* factory methods annotated with {@link DynamicPropertySource @DynamicPropertySource}.
*
* @author Sam Brannen
* @since 6.2
*/
class DynamicPropertySourceBeanInitializer implements BeanFactoryInitializer<ListableBeanFactory> {
private static final Log logger = LogFactory.getLog(DynamicPropertySourceBeanInitializer.class);
@Override
public void initialize(ListableBeanFactory beanFactory) {
for (String name : beanFactory.getBeanNamesForAnnotation(DynamicPropertySource.class)) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly initializing @DynamicPropertySource bean '%s'".formatted(name));
}
beanFactory.getBean(name);
}
}
}

View File

@ -17,11 +17,17 @@
package org.springframework.test.context.support;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Supplier;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.lang.Nullable;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.util.Assert;
import org.springframework.util.function.SupplierUtils;
/**
@ -36,9 +42,20 @@ class DynamicValuesPropertySource extends MapPropertySource {
static final String PROPERTY_SOURCE_NAME = "Dynamic Test Properties";
final DynamicPropertyRegistry dynamicPropertyRegistry;
DynamicValuesPropertySource() {
this(Collections.synchronizedMap(new LinkedHashMap<>()));
}
DynamicValuesPropertySource(Map<String, Supplier<Object>> valueSuppliers) {
super(PROPERTY_SOURCE_NAME, Collections.unmodifiableMap(valueSuppliers));
this.dynamicPropertyRegistry = (name, valueSupplier) -> {
Assert.hasText(name, "'name' must not be null or blank");
Assert.notNull(valueSupplier, "'valueSupplier' must not be null");
valueSuppliers.put(name, valueSupplier);
};
}
@ -48,4 +65,27 @@ class DynamicValuesPropertySource extends MapPropertySource {
return SupplierUtils.resolve(super.getProperty(name));
}
/**
* Get the {@code DynamicValuesPropertySource} registered in the environment
* or create and register a new {@code DynamicValuesPropertySource} in the
* environment.
*/
static DynamicValuesPropertySource getOrCreate(ConfigurableEnvironment environment) {
MutablePropertySources propertySources = environment.getPropertySources();
PropertySource<?> propertySource = propertySources.get(PROPERTY_SOURCE_NAME);
if (propertySource instanceof DynamicValuesPropertySource dynamicValuesPropertySource) {
return dynamicValuesPropertySource;
}
else if (propertySource == null) {
DynamicValuesPropertySource dynamicValuesPropertySource = new DynamicValuesPropertySource();
propertySources.addFirst(dynamicValuesPropertySource);
return dynamicValuesPropertySource;
}
else {
throw new IllegalStateException("PropertySource with name '%s' must be a DynamicValuesPropertySource"
.formatted(PROPERTY_SOURCE_NAME));
}
}
}

View File

@ -41,7 +41,7 @@ import org.junit.platform.suite.api.Suite;
@Suite
@SelectClasses(
value = {
DynamicPropertyRegistryIntegrationTests.class,
DynamicPropertyRegistrarIntegrationTests.class,
DynamicPropertySourceIntegrationTests.class
},
names = {

View File

@ -29,60 +29,77 @@ 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.assertj.core.api.Assertions.assertThatRuntimeException;
/**
* Integration tests for {@link DynamicPropertyRegistry} bean support.
* Integration tests for {@link DynamicPropertyRegistrar} bean support.
*
* @author Sam Brannen
* @since 6.2
* @see DynamicPropertySourceIntegrationTests
*/
@SpringJUnitConfig
@TestPropertySource(properties = "api.url: https://example.com/test")
class DynamicPropertyRegistryIntegrationTests {
@TestPropertySource(properties = "api.url.1: https://example.com/test")
class DynamicPropertyRegistrarIntegrationTests {
private static final String API_URL = "api.url";
private static final String API_URL_1 = "api.url.1";
private static final String API_URL_2 = "api.url.2";
@Test
void customDynamicPropertyRegistryCanExistInApplicationContext(
@Autowired DynamicPropertyRegistry dynamicPropertyRegistry) {
assertThatRuntimeException()
.isThrownBy(() -> dynamicPropertyRegistry.add("test", () -> "test"))
.withMessage("Boom!");
}
@Test
void dynamicPropertySourceOverridesTestPropertySource(@Autowired ConfigurableEnvironment env) {
assertApiUrlIsDynamic(env.getProperty(API_URL));
assertApiUrlIsDynamic1(env.getProperty(API_URL_1));
MutablePropertySources propertySources = env.getPropertySources();
assertThat(propertySources.size()).isGreaterThanOrEqualTo(4);
assertThat(propertySources.contains("Inlined Test Properties")).isTrue();
assertThat(propertySources.contains("Dynamic Test Properties")).isTrue();
assertThat(propertySources.get("Inlined Test Properties").getProperty(API_URL)).isEqualTo("https://example.com/test");
assertThat(propertySources.get("Dynamic Test Properties").getProperty(API_URL)).isEqualTo("https://example.com/dynamic");
assertThat(propertySources.get("Inlined Test Properties").getProperty(API_URL_1)).isEqualTo("https://example.com/test");
assertThat(propertySources.get("Dynamic Test Properties").getProperty(API_URL_1)).isEqualTo("https://example.com/dynamic/1");
assertThat(propertySources.get("Dynamic Test Properties").getProperty(API_URL_2)).isEqualTo("https://example.com/dynamic/2");
}
@Test
void testReceivesDynamicProperty(@Value("${api.url}") String apiUrl) {
assertApiUrlIsDynamic(apiUrl);
void testReceivesDynamicProperties(@Value("${api.url.1}") String apiUrl1, @Value("${api.url.2}") String apiUrl2) {
assertApiUrlIsDynamic1(apiUrl1);
assertApiUrlIsDynamic2(apiUrl2);
}
@Test
void environmentInjectedServiceCanRetrieveDynamicProperty(@Autowired EnvironmentInjectedService service) {
assertApiUrlIsDynamic(service);
assertApiUrlIsDynamic1(service);
}
@Test
void constructorInjectedServiceReceivesDynamicProperty(@Autowired ConstructorInjectedService service) {
assertApiUrlIsDynamic(service);
assertApiUrlIsDynamic1(service);
}
@Test
void setterInjectedServiceReceivesDynamicProperty(@Autowired SetterInjectedService service) {
assertApiUrlIsDynamic(service);
assertApiUrlIsDynamic1(service);
}
private static void assertApiUrlIsDynamic(ApiUrlClient service) {
assertApiUrlIsDynamic(service.getApiUrl());
private static void assertApiUrlIsDynamic1(ApiUrlClient service) {
assertApiUrlIsDynamic1(service.getApiUrl());
}
private static void assertApiUrlIsDynamic(String apiUrl) {
assertThat(apiUrl).isEqualTo("https://example.com/dynamic");
private static void assertApiUrlIsDynamic1(String apiUrl) {
assertThat(apiUrl).isEqualTo("https://example.com/dynamic/1");
}
private static void assertApiUrlIsDynamic2(String apiUrl) {
assertThat(apiUrl).isEqualTo("https://example.com/dynamic/2");
}
@ -90,16 +107,30 @@ class DynamicPropertyRegistryIntegrationTests {
@Import({ EnvironmentInjectedService.class, ConstructorInjectedService.class, SetterInjectedService.class })
static class Config {
// Annotating this @Bean method with @DynamicPropertySource ensures that
// this bean will be instantiated before any other singleton beans in the
@Bean
ApiServer apiServer() {
return new ApiServer();
}
// Accepting ApiServer as a method argument ensures that the apiServer
// bean will be instantiated before any other singleton beans in the
// context which further ensures that the dynamic "api.url" property is
// available to all standard singleton beans.
@Bean
@DynamicPropertySource
ApiServer apiServer(DynamicPropertyRegistry registry) {
ApiServer apiServer = new ApiServer();
registry.add(API_URL, apiServer::getUrl);
return apiServer;
DynamicPropertyRegistrar apiServerProperties1(ApiServer apiServer) {
return registry -> registry.add(API_URL_1, () -> apiServer.getUrl() + "/1");
}
@Bean
DynamicPropertyRegistrar apiServerProperties2(ApiServer apiServer) {
return registry -> registry.add(API_URL_2, () -> apiServer.getUrl() + "/2");
}
@Bean
DynamicPropertyRegistry dynamicPropertyRegistry() {
return (name, valueSupplier) -> {
throw new RuntimeException("Boom!");
};
}
}
@ -120,7 +151,7 @@ class DynamicPropertyRegistryIntegrationTests {
@Override
public String getApiUrl() {
return this.env.getProperty(API_URL);
return this.env.getProperty(API_URL_1);
}
}
@ -129,7 +160,7 @@ class DynamicPropertyRegistryIntegrationTests {
private final String apiUrl;
ConstructorInjectedService(@Value("${api.url}") String apiUrl) {
ConstructorInjectedService(@Value("${api.url.1}") String apiUrl) {
this.apiUrl = apiUrl;
}
@ -145,7 +176,7 @@ class DynamicPropertyRegistryIntegrationTests {
@Autowired
void setApiUrl(@Value("${api.url}") String apiUrl) {
void setApiUrl(@Value("${api.url.1}") String apiUrl) {
this.apiUrl = apiUrl;
}

View File

@ -23,6 +23,7 @@ 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.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.env.ConfigurableEnvironment;
@ -30,14 +31,16 @@ 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.assertj.core.api.Assertions.assertThatRuntimeException;
import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
/**
* Integration tests for {@link DynamicPropertySource @DynamicPropertySource}.
* Integration tests for {@link DynamicPropertySource @DynamicPropertySource} and
* {@link DynamicPropertyRegistrar} bean support.
*
* @author Phillip Webb
* @author Sam Brannen
* @see DynamicPropertyRegistryIntegrationTests
* @see DynamicPropertyRegistrarIntegrationTests
*/
@SpringJUnitConfig
@TestPropertySource(properties = "test.container.ip: test")
@ -46,6 +49,7 @@ import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
class DynamicPropertySourceIntegrationTests {
private static final String TEST_CONTAINER_IP = "test.container.ip";
private static final String MAGIC_WORD = "magic.word";
static {
System.setProperty(TEST_CONTAINER_IP, "system");
@ -54,8 +58,12 @@ class DynamicPropertySourceIntegrationTests {
static final DemoContainer container = new DemoContainer();
@DynamicPropertySource
static void containerProperties(DynamicPropertyRegistry registry) {
static void containerPropertiesIpAddress(DynamicPropertyRegistry registry) {
registry.add(TEST_CONTAINER_IP, container::getIpAddress);
}
@DynamicPropertySource
static void containerPropertiesPort(DynamicPropertyRegistry registry) {
registry.add("test.container.port", container::getPort);
}
@ -65,6 +73,16 @@ class DynamicPropertySourceIntegrationTests {
System.clearProperty(TEST_CONTAINER_IP);
}
@Test
@DisplayName("A custom DynamicPropertyRegistry bean can exist in the ApplicationContext")
void customDynamicPropertyRegistryCanExistInApplicationContext(
@Autowired DynamicPropertyRegistry dynamicPropertyRegistry) {
assertThatRuntimeException()
.isThrownBy(() -> dynamicPropertyRegistry.add("test", () -> "test"))
.withMessage("Boom!");
}
@Test
@DisplayName("@DynamicPropertySource overrides @TestPropertySource and JVM system property")
void dynamicPropertySourceOverridesTestPropertySourceAndSystemProperty(@Autowired ConfigurableEnvironment env) {
@ -74,9 +92,11 @@ class DynamicPropertySourceIntegrationTests {
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("Dynamic Test Properties").getProperty(MAGIC_WORD)).isEqualTo("enigma");
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");
assertThat(env.getProperty(MAGIC_WORD)).isEqualTo("enigma");
}
@Test
@ -90,6 +110,19 @@ class DynamicPropertySourceIntegrationTests {
@Configuration
@Import(Service.class)
static class Config {
@Bean
DynamicPropertyRegistrar magicWordProperties() {
return registry -> registry.add(MAGIC_WORD, () -> "enigma");
}
@Bean
DynamicPropertyRegistry dynamicPropertyRegistry() {
return (name, valueSupplier) -> {
throw new RuntimeException("Boom!");
};
}
}
static class Service {

View File

@ -40,7 +40,7 @@ abstract class AbstractAotTests {
"org/springframework/test/context/aot/samples/basic/BasicSpringJupiterImportedConfigTests__TestContext001_BeanDefinitions.java",
"org/springframework/test/context/aot/samples/basic/BasicSpringJupiterImportedConfigTests__TestContext001_BeanFactoryRegistrations.java",
"org/springframework/test/context/aot/samples/basic/BasicTestConfiguration__TestContext001_BeanDefinitions.java",
"org/springframework/test/context/support/DynamicPropertySourceBeanInitializer__TestContext001_BeanDefinitions.java",
"org/springframework/test/context/support/DynamicPropertyRegistrarBeanInitializer__TestContext001_BeanDefinitions.java",
// BasicSpringJupiterSharedConfigTests
"org/springframework/context/event/DefaultEventListenerFactory__TestContext002_BeanDefinitions.java",
"org/springframework/context/event/EventListenerMethodProcessor__TestContext002_BeanDefinitions.java",
@ -51,7 +51,7 @@ abstract class AbstractAotTests {
"org/springframework/test/context/aot/samples/basic/BasicTestConfiguration__TestContext002_BeanDefinitions.java",
"org/springframework/test/context/aot/samples/management/ManagementConfiguration__TestContext002_BeanDefinitions.java",
"org/springframework/test/context/aot/samples/management/ManagementMessageService__TestContext002_ManagementBeanDefinitions.java",
"org/springframework/test/context/support/DynamicPropertySourceBeanInitializer__TestContext002_BeanDefinitions.java",
"org/springframework/test/context/support/DynamicPropertyRegistrarBeanInitializer__TestContext002_BeanDefinitions.java",
// BasicSpringJupiterTests -- not generated b/c already generated for BasicSpringJupiterSharedConfigTests.
// BasicSpringJupiterTests.NestedTests
"org/springframework/context/event/DefaultEventListenerFactory__TestContext003_BeanDefinitions.java",
@ -63,28 +63,28 @@ abstract class AbstractAotTests {
"org/springframework/test/context/aot/samples/basic/BasicTestConfiguration__TestContext003_BeanDefinitions.java",
"org/springframework/test/context/aot/samples/management/ManagementConfiguration__TestContext003_BeanDefinitions.java",
"org/springframework/test/context/aot/samples/management/ManagementMessageService__TestContext003_ManagementBeanDefinitions.java",
"org/springframework/test/context/support/DynamicPropertySourceBeanInitializer__TestContext003_BeanDefinitions.java",
"org/springframework/test/context/support/DynamicPropertyRegistrarBeanInitializer__TestContext003_BeanDefinitions.java",
// BasicSpringTestNGTests
"org/springframework/context/event/DefaultEventListenerFactory__TestContext004_BeanDefinitions.java",
"org/springframework/context/event/EventListenerMethodProcessor__TestContext004_BeanDefinitions.java",
"org/springframework/test/context/aot/samples/basic/BasicSpringTestNGTests__TestContext004_ApplicationContextInitializer.java",
"org/springframework/test/context/aot/samples/basic/BasicSpringTestNGTests__TestContext004_BeanFactoryRegistrations.java",
"org/springframework/test/context/aot/samples/basic/BasicTestConfiguration__TestContext004_BeanDefinitions.java",
"org/springframework/test/context/support/DynamicPropertySourceBeanInitializer__TestContext004_BeanDefinitions.java",
"org/springframework/test/context/support/DynamicPropertyRegistrarBeanInitializer__TestContext004_BeanDefinitions.java",
// BasicSpringVintageTests
"org/springframework/context/event/DefaultEventListenerFactory__TestContext005_BeanDefinitions.java",
"org/springframework/context/event/EventListenerMethodProcessor__TestContext005_BeanDefinitions.java",
"org/springframework/test/context/aot/samples/basic/BasicSpringVintageTests__TestContext005_ApplicationContextInitializer.java",
"org/springframework/test/context/aot/samples/basic/BasicSpringVintageTests__TestContext005_BeanFactoryRegistrations.java",
"org/springframework/test/context/aot/samples/basic/BasicTestConfiguration__TestContext005_BeanDefinitions.java",
"org/springframework/test/context/support/DynamicPropertySourceBeanInitializer__TestContext005_BeanDefinitions.java",
"org/springframework/test/context/support/DynamicPropertyRegistrarBeanInitializer__TestContext005_BeanDefinitions.java",
// DisabledInAotRuntimeMethodLevelTests
"org/springframework/context/event/DefaultEventListenerFactory__TestContext006_BeanDefinitions.java",
"org/springframework/context/event/EventListenerMethodProcessor__TestContext006_BeanDefinitions.java",
"org/springframework/test/context/aot/samples/basic/DisabledInAotRuntimeMethodLevelTests__TestContext006_ApplicationContextInitializer.java",
"org/springframework/test/context/aot/samples/basic/DisabledInAotRuntimeMethodLevelTests__TestContext006_BeanDefinitions.java",
"org/springframework/test/context/aot/samples/basic/DisabledInAotRuntimeMethodLevelTests__TestContext006_BeanFactoryRegistrations.java",
"org/springframework/test/context/support/DynamicPropertySourceBeanInitializer__TestContext006_BeanDefinitions.java"
"org/springframework/test/context/support/DynamicPropertyRegistrarBeanInitializer__TestContext006_BeanDefinitions.java"
};
Stream<Class<?>> scan() {

View File

@ -395,7 +395,7 @@ class TestContextAotGeneratorIntegrationTests extends AbstractAotTests {
"org/springframework/test/context/aot/samples/basic/BasicTestConfiguration__TestContext001_BeanDefinitions.java",
"org/springframework/test/context/aot/samples/management/ManagementConfiguration__TestContext001_BeanDefinitions.java",
"org/springframework/test/context/aot/samples/management/ManagementMessageService__TestContext001_ManagementBeanDefinitions.java",
"org/springframework/test/context/support/DynamicPropertySourceBeanInitializer__TestContext001_BeanDefinitions.java",
"org/springframework/test/context/support/DynamicPropertyRegistrarBeanInitializer__TestContext001_BeanDefinitions.java",
// BasicSpringJupiterTests -- not generated b/c already generated for BasicSpringJupiterSharedConfigTests.
// BasicSpringJupiterTests.NestedTests
"org/springframework/context/event/DefaultEventListenerFactory__TestContext002_BeanDefinitions.java",
@ -407,28 +407,28 @@ class TestContextAotGeneratorIntegrationTests extends AbstractAotTests {
"org/springframework/test/context/aot/samples/basic/BasicTestConfiguration__TestContext002_BeanDefinitions.java",
"org/springframework/test/context/aot/samples/management/ManagementConfiguration__TestContext002_BeanDefinitions.java",
"org/springframework/test/context/aot/samples/management/ManagementMessageService__TestContext002_ManagementBeanDefinitions.java",
"org/springframework/test/context/support/DynamicPropertySourceBeanInitializer__TestContext002_BeanDefinitions.java",
"org/springframework/test/context/support/DynamicPropertyRegistrarBeanInitializer__TestContext002_BeanDefinitions.java",
// BasicSpringTestNGTests
"org/springframework/context/event/DefaultEventListenerFactory__TestContext003_BeanDefinitions.java",
"org/springframework/context/event/EventListenerMethodProcessor__TestContext003_BeanDefinitions.java",
"org/springframework/test/context/aot/samples/basic/BasicSpringTestNGTests__TestContext003_ApplicationContextInitializer.java",
"org/springframework/test/context/aot/samples/basic/BasicSpringTestNGTests__TestContext003_BeanFactoryRegistrations.java",
"org/springframework/test/context/aot/samples/basic/BasicTestConfiguration__TestContext003_BeanDefinitions.java",
"org/springframework/test/context/support/DynamicPropertySourceBeanInitializer__TestContext003_BeanDefinitions.java",
"org/springframework/test/context/support/DynamicPropertyRegistrarBeanInitializer__TestContext003_BeanDefinitions.java",
// BasicSpringVintageTests
"org/springframework/context/event/DefaultEventListenerFactory__TestContext004_BeanDefinitions.java",
"org/springframework/context/event/EventListenerMethodProcessor__TestContext004_BeanDefinitions.java",
"org/springframework/test/context/aot/samples/basic/BasicSpringVintageTests__TestContext004_ApplicationContextInitializer.java",
"org/springframework/test/context/aot/samples/basic/BasicSpringVintageTests__TestContext004_BeanFactoryRegistrations.java",
"org/springframework/test/context/aot/samples/basic/BasicTestConfiguration__TestContext004_BeanDefinitions.java",
"org/springframework/test/context/support/DynamicPropertySourceBeanInitializer__TestContext004_BeanDefinitions.java",
"org/springframework/test/context/support/DynamicPropertyRegistrarBeanInitializer__TestContext004_BeanDefinitions.java",
// SqlScriptsSpringJupiterTests
"org/springframework/context/event/DefaultEventListenerFactory__TestContext005_BeanDefinitions.java",
"org/springframework/context/event/EventListenerMethodProcessor__TestContext005_BeanDefinitions.java",
"org/springframework/test/context/aot/samples/jdbc/SqlScriptsSpringJupiterTests__TestContext005_ApplicationContextInitializer.java",
"org/springframework/test/context/aot/samples/jdbc/SqlScriptsSpringJupiterTests__TestContext005_BeanFactoryRegistrations.java",
"org/springframework/test/context/jdbc/EmptyDatabaseConfig__TestContext005_BeanDefinitions.java",
"org/springframework/test/context/support/DynamicPropertySourceBeanInitializer__TestContext005_BeanDefinitions.java",
"org/springframework/test/context/support/DynamicPropertyRegistrarBeanInitializer__TestContext005_BeanDefinitions.java",
// WebSpringJupiterTests
"org/springframework/context/event/DefaultEventListenerFactory__TestContext006_BeanDefinitions.java",
"org/springframework/context/event/EventListenerMethodProcessor__TestContext006_BeanDefinitions.java",
@ -437,14 +437,14 @@ class TestContextAotGeneratorIntegrationTests extends AbstractAotTests {
"org/springframework/test/context/aot/samples/web/WebTestConfiguration__TestContext006_BeanDefinitions.java",
"org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration__TestContext006_Autowiring.java",
"org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration__TestContext006_BeanDefinitions.java",
"org/springframework/test/context/support/DynamicPropertySourceBeanInitializer__TestContext006_BeanDefinitions.java",
"org/springframework/test/context/support/DynamicPropertyRegistrarBeanInitializer__TestContext006_BeanDefinitions.java",
// XmlSpringJupiterTests
"org/springframework/context/event/DefaultEventListenerFactory__TestContext007_BeanDefinitions.java",
"org/springframework/context/event/EventListenerMethodProcessor__TestContext007_BeanDefinitions.java",
"org/springframework/test/context/aot/samples/common/DefaultMessageService__TestContext007_BeanDefinitions.java",
"org/springframework/test/context/aot/samples/xml/XmlSpringJupiterTests__TestContext007_ApplicationContextInitializer.java",
"org/springframework/test/context/aot/samples/xml/XmlSpringJupiterTests__TestContext007_BeanFactoryRegistrations.java",
"org/springframework/test/context/support/DynamicPropertySourceBeanInitializer__TestContext007_BeanDefinitions.java",
"org/springframework/test/context/support/DynamicPropertyRegistrarBeanInitializer__TestContext007_BeanDefinitions.java",
};
}