From ea7a1d789e6d07ae18e699c718cbf54dacfb91de Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Wed, 10 Dec 2025 18:34:25 +0100 Subject: [PATCH] Resolve ContextLoader only once in AbstractTestContextBootstrapper Closes gh-35994 --- .../AbstractTestContextBootstrapper.java | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java b/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java index 4075e367d3a..d2f4f1310c0 100644 --- a/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java +++ b/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java @@ -231,7 +231,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot Class declaringClass = reversedList.get(0).getDeclaringClass(); mergedConfig = buildMergedContextConfiguration( - declaringClass, reversedList, parentConfig, cacheAwareContextLoaderDelegate, true); + declaringClass, reversedList, null, parentConfig, cacheAwareContextLoaderDelegate, true); parentConfig = mergedConfig; } @@ -242,7 +242,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot else { return buildMergedContextConfiguration(testClass, ContextLoaderUtils.resolveContextConfigurationAttributes(testClass), - null, cacheAwareContextLoaderDelegate, true); + null, null, cacheAwareContextLoaderDelegate, true); } } @@ -263,7 +263,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot "Neither @ContextConfiguration nor @ContextHierarchy found for test class [%s]: using %s", testClass.getSimpleName(), contextLoader.getClass().getSimpleName())); } - return buildMergedContextConfiguration(testClass, defaultConfigAttributesList, null, + return buildMergedContextConfiguration(testClass, defaultConfigAttributesList, contextLoader, null, cacheAwareContextLoaderDelegate, false); } @@ -277,6 +277,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot * specified test class, ordered bottom-up (i.e., as if we were * traversing up the class hierarchy and enclosing class hierarchy); never * {@code null} or empty + * @param contextLoader a pre-resolved {@link ContextLoader} to use; may be {@code null} * @param parentConfig the merged context configuration for the parent application * context in a context hierarchy, or {@code null} if there is no parent * @param cacheAwareContextLoaderDelegate the cache-aware context loader delegate to @@ -294,13 +295,16 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot * @see MergedContextConfiguration */ private MergedContextConfiguration buildMergedContextConfiguration(Class testClass, - List configAttributesList, @Nullable MergedContextConfiguration parentConfig, + List configAttributesList, @Nullable ContextLoader contextLoader, + @Nullable MergedContextConfiguration parentConfig, CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, boolean requireLocationsClassesOrInitializers) { Assert.notEmpty(configAttributesList, "ContextConfigurationAttributes list must not be null or empty"); - ContextLoader contextLoader = resolveContextLoader(testClass, configAttributesList); + if (contextLoader == null) { + contextLoader = resolveContextLoader(testClass, configAttributesList); + } List locations = new ArrayList<>(); List> classes = new ArrayList<>(); List> initializers = new ArrayList<>(); @@ -331,11 +335,12 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot Set contextCustomizers = getContextCustomizers(testClass, Collections.unmodifiableList(configAttributesList)); + ContextLoader effectivelyFinalContextLoader = contextLoader; Assert.state(!(requireLocationsClassesOrInitializers && - areAllEmpty(locations, classes, initializers, contextCustomizers)), () -> String.format( - "%s was unable to detect defaults, and no ApplicationContextInitializers " + - "or ContextCustomizers were declared for context configuration attributes %s", - contextLoader.getClass().getSimpleName(), configAttributesList)); + areAllEmpty(locations, classes, initializers, contextCustomizers)), () -> """ + %s was unable to detect defaults, and no ApplicationContextInitializers \ + or ContextCustomizers were declared for context configuration attributes %s\ + """.formatted(effectivelyFinalContextLoader.getClass().getSimpleName(), configAttributesList)); MergedTestPropertySources mergedTestPropertySources = TestPropertySourceUtils.buildMergedTestPropertySources(testClass);