diff --git a/spring-test/src/main/java/org/springframework/test/context/ContextCustomizer.java b/spring-test/src/main/java/org/springframework/test/context/ContextCustomizer.java new file mode 100644 index 0000000000..05e2c09dd0 --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/context/ContextCustomizer.java @@ -0,0 +1,49 @@ +/* + * Copyright 2002-2016 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 + * + * http://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; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; + +/** + * Strategy interface for customizing {@link ApplicationContext application contexts} that + * are created and managed by the Spring TestContext Framework. + * + *

Customizers are loaded via {@link ContextCustomizerFactory} classes registered in + * {@code spring.factories}. + * + *

Implementations should take care to implement correct {@code equals} and + * {@code hashCode} methods since customizers form part of the + * {@link MergedContextConfiguration} which is used as a cache key. + * + * @author Phillip Webb + * @since 4.3 + * @see ContextCustomizerFactory + * @see org.springframework.test.context.support.AbstractContextLoader + */ +public interface ContextCustomizer { + + /** + * Called before bean definitions are read to customize the + * {@link ConfigurableApplicationContext}. + * @param context the context that should be prepared + * @param mergedContextConfiguration the merged context configuration + */ + void customizeContext(ConfigurableApplicationContext context, + MergedContextConfiguration mergedContextConfiguration); + +} diff --git a/spring-test/src/main/java/org/springframework/test/context/ContextCustomizerFactory.java b/spring-test/src/main/java/org/springframework/test/context/ContextCustomizerFactory.java new file mode 100644 index 0000000000..f9c77277b3 --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/context/ContextCustomizerFactory.java @@ -0,0 +1,46 @@ +/* + * Copyright 2002-2016 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 + * + * http://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; + +import java.util.List; + +import org.springframework.context.ConfigurableApplicationContext; + +/** + * Factory registered in {@code spring.factories} that is used to create + * {@link ContextCustomizer ContextCustomizers}. Factories are called after + * {@link ContextLoader ContextLoaders} have been triggered but before the + * {@link MergedContextConfiguration} is created. + * + * @author Phillip Webb + * @since 4.3 + */ +public interface ContextCustomizerFactory { + + /** + * Get the {@link ContextCustomizer} (if any) that should be used to customize the + * {@link ConfigurableApplicationContext} when it is created. + * @param testClass the test class + * @param configurationAttributes he list of context configuration attributes for the + * test class, ordered bottom-up (i.e., as if we were traversing up the class + * hierarchy); never {@code null} or empty. + * @return a {@link ContextCustomizer} or {@code null} + */ + ContextCustomizer getContextCustomizer(Class testClass, + List configurationAttributes); + +} diff --git a/spring-test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java b/spring-test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java index 5090d7c5a4..1476f5863b 100644 --- a/spring-test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java +++ b/spring-test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java @@ -89,6 +89,8 @@ public class MergedContextConfiguration implements Serializable { private final String[] propertySourceProperties; + private final Set contextCustomizers; + private final ContextLoader contextLoader; private final CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate; @@ -201,8 +203,8 @@ public class MergedContextConfiguration implements Serializable { public MergedContextConfiguration(MergedContextConfiguration mergedConfig) { this(mergedConfig.testClass, mergedConfig.locations, mergedConfig.classes, mergedConfig.contextInitializerClasses, mergedConfig.activeProfiles, mergedConfig.propertySourceLocations, - mergedConfig.propertySourceProperties, mergedConfig.contextLoader, - mergedConfig.cacheAwareContextLoaderDelegate, mergedConfig.parent); + mergedConfig.propertySourceProperties, mergedConfig.contextCustomizers, + mergedConfig.contextLoader, mergedConfig.cacheAwareContextLoaderDelegate, mergedConfig.parent); } /** @@ -233,6 +235,40 @@ public class MergedContextConfiguration implements Serializable { String[] activeProfiles, String[] propertySourceLocations, String[] propertySourceProperties, ContextLoader contextLoader, CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, MergedContextConfiguration parent) { + this(testClass, locations, classes, contextInitializerClasses, activeProfiles, + propertySourceLocations, propertySourceProperties, + Collections. emptySet(), contextLoader, + cacheAwareContextLoaderDelegate, parent); + } + + /** + * Create a new {@code MergedContextConfiguration} instance for the + * supplied parameters. + *

If a {@code null} value is supplied for {@code locations}, + * {@code classes}, {@code activeProfiles}, {@code propertySourceLocations}, + * or {@code propertySourceProperties} an empty array will be stored instead. + * If a {@code null} value is supplied for the + * {@code contextInitializerClasses} an empty set will be stored instead. + * Furthermore, active profiles will be sorted, and duplicate profiles + * will be removed. + * @param testClass the test class for which the configuration was merged + * @param locations the merged context resource locations + * @param classes the merged annotated classes + * @param contextInitializerClasses the merged context initializer classes + * @param activeProfiles the merged active bean definition profiles + * @param propertySourceLocations the merged {@code PropertySource} locations + * @param propertySourceProperties the merged {@code PropertySource} properties + * @param contextLoader the resolved {@code ContextLoader} + * @param cacheAwareContextLoaderDelegate a cache-aware context loader + * delegate with which to retrieve the parent context + * @param parent the parent configuration or {@code null} if there is no parent + * @since 4.2 + */ + public MergedContextConfiguration(Class testClass, String[] locations, Class[] classes, + Set>> contextInitializerClasses, + String[] activeProfiles, String[] propertySourceLocations, String[] propertySourceProperties, + Set contextCustomizers, ContextLoader contextLoader, + CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, MergedContextConfiguration parent) { this.testClass = testClass; this.locations = processStrings(locations); @@ -241,6 +277,7 @@ public class MergedContextConfiguration implements Serializable { this.activeProfiles = processActiveProfiles(activeProfiles); this.propertySourceLocations = processStrings(propertySourceLocations); this.propertySourceProperties = processStrings(propertySourceProperties); + this.contextCustomizers = Collections.unmodifiableSet(contextCustomizers); this.contextLoader = contextLoader; this.cacheAwareContextLoaderDelegate = cacheAwareContextLoaderDelegate; this.parent = parent; @@ -348,6 +385,14 @@ public class MergedContextConfiguration implements Serializable { return this.propertySourceProperties; } + /** + * Get the merged {@link ContextCustomizer ContextCustomizers} that will be applied + * when the application context is loaded. + */ + public Set getContextCustomizers() { + return contextCustomizers; + } + /** * Get the resolved {@link ContextLoader} for the {@linkplain #getTestClass() test class}. */ @@ -424,6 +469,9 @@ public class MergedContextConfiguration implements Serializable { if (!Arrays.equals(this.propertySourceProperties, otherConfig.propertySourceProperties)) { return false; } + if (!this.contextCustomizers.equals(otherConfig.contextCustomizers)) { + return false; + } if (this.parent == null) { if (otherConfig.parent != null) { @@ -454,6 +502,7 @@ public class MergedContextConfiguration implements Serializable { result = 31 * result + Arrays.hashCode(this.activeProfiles); result = 31 * result + Arrays.hashCode(this.propertySourceLocations); result = 31 * result + Arrays.hashCode(this.propertySourceProperties); + result = 31 * result + this.contextCustomizers.hashCode(); result = 31 * result + (this.parent != null ? this.parent.hashCode() : 0); result = 31 * result + nullSafeToString(this.contextLoader).hashCode(); return result; diff --git a/spring-test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java b/spring-test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java index c1198d0b10..279bccf2d6 100644 --- a/spring-test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java +++ b/spring-test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java @@ -28,11 +28,13 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextException; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.GenericApplicationContext; import org.springframework.core.GenericTypeResolver; import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.env.PropertySource; import org.springframework.core.io.ClassPathResource; import org.springframework.test.context.ContextConfigurationAttributes; +import org.springframework.test.context.ContextCustomizer; import org.springframework.test.context.ContextLoader; import org.springframework.test.context.MergedContextConfiguration; import org.springframework.test.context.SmartContextLoader; @@ -107,6 +109,8 @@ public abstract class AbstractContextLoader implements SmartContextLoader { * {@linkplain MergedContextConfiguration#getPropertySourceProperties() * inlined properties} from the supplied {@code MergedContextConfiguration} * to the {@code Environment} of the context. + *

  • Calls any {@link MergedContextConfiguration#getContextCustomizers() + * ContextCustomizers} that are part of the {@link MergedContextConfiguration}.
  • *
  • Determines what (if any) context initializer classes have been supplied * via the {@code MergedContextConfiguration} and instantiates and * {@linkplain ApplicationContextInitializer#initialize invokes} each with the @@ -167,6 +171,25 @@ public abstract class AbstractContextLoader implements SmartContextLoader { } } + /** + * Customize the {@link ConfigurableApplicationContext} created by this + * {@code ContextLoader} after bean definitions have been + * loaded into the context but before the context is refreshed. + * + *

    The default implementation triggers all the + * {@link MergedContextConfiguration#getContextCustomizers() context customizers} that + * have been registered with the {@code mergedConfig}. + * + * @param context the newly created application context + * @param mergedConfig the merged context configuration + * @since 4.3 + */ + protected void customizeContext(GenericApplicationContext context, MergedContextConfiguration mergedConfig) { + for (ContextCustomizer contextCustomizer : mergedConfig.getContextCustomizers()) { + contextCustomizer.customizeContext(context, mergedConfig); + } + } + // --- ContextLoader ------------------------------------------------------- diff --git a/spring-test/src/main/java/org/springframework/test/context/support/AbstractDelegatingSmartContextLoader.java b/spring-test/src/main/java/org/springframework/test/context/support/AbstractDelegatingSmartContextLoader.java index 8f942194f9..4fb04597cb 100644 --- a/spring-test/src/main/java/org/springframework/test/context/support/AbstractDelegatingSmartContextLoader.java +++ b/spring-test/src/main/java/org/springframework/test/context/support/AbstractDelegatingSmartContextLoader.java @@ -254,7 +254,7 @@ public abstract class AbstractDelegatingSmartContextLoader implements SmartConte // If neither of the candidates supports the mergedConfig based on resources but // ACIs were declared, then delegate to the annotation config loader. - if (!mergedConfig.getContextInitializerClasses().isEmpty()) { + if (!mergedConfig.getContextInitializerClasses().isEmpty() || !mergedConfig.getContextCustomizers().isEmpty()) { return delegateLoading(getAnnotationConfigLoader(), mergedConfig); } diff --git a/spring-test/src/main/java/org/springframework/test/context/support/AbstractGenericContextLoader.java b/spring-test/src/main/java/org/springframework/test/context/support/AbstractGenericContextLoader.java index 41e42fcf4e..3b924f0879 100644 --- a/spring-test/src/main/java/org/springframework/test/context/support/AbstractGenericContextLoader.java +++ b/spring-test/src/main/java/org/springframework/test/context/support/AbstractGenericContextLoader.java @@ -122,6 +122,7 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader loadBeanDefinitions(context, mergedConfig); AnnotationConfigUtils.registerAnnotationConfigProcessors(context); customizeContext(context); + customizeContext(context, mergedConfig); context.refresh(); context.registerShutdownHook(); return context; 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 ab87d056f7..77e375cb72 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 @@ -38,6 +38,8 @@ import org.springframework.test.context.BootstrapContext; import org.springframework.test.context.CacheAwareContextLoaderDelegate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfigurationAttributes; +import org.springframework.test.context.ContextCustomizer; +import org.springframework.test.context.ContextCustomizerFactory; import org.springframework.test.context.ContextHierarchy; import org.springframework.test.context.ContextLoader; import org.springframework.test.context.MergedContextConfiguration; @@ -385,10 +387,13 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot } } - if (requireLocationsClassesOrInitializers && areAllEmpty(locations, classes, initializers)) { + Set contextCustomizers = getContextCustomizers(testClass, + Collections.unmodifiableList(configAttributesList)); + + if (requireLocationsClassesOrInitializers && areAllEmpty(locations, classes, initializers, contextCustomizers)) { throw new IllegalStateException(String.format( "%s was unable to detect defaults, and no ApplicationContextInitializers " - + "were declared for context configuration attributes %s", + + "or ContextCustomizers were declared for context configuration attributes %s", contextLoader.getClass().getSimpleName(), configAttributesList)); } @@ -400,14 +405,32 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot ActiveProfilesUtils.resolveActiveProfiles(testClass), mergedTestPropertySources.getLocations(), mergedTestPropertySources.getProperties(), - contextLoader, cacheAwareContextLoaderDelegate, parentConfig); + contextCustomizers, contextLoader, cacheAwareContextLoaderDelegate, parentConfig); return processMergedContextConfiguration(mergedConfig); } + private Set getContextCustomizers(Class testClass, + List configurationAttributes) { + List factories = geContextCustomizerFactories(); + Set customizers = new LinkedHashSet(factories.size()); + for (ContextCustomizerFactory factory : factories) { + ContextCustomizer customizer = factory.getContextCustomizer(testClass, configurationAttributes); + if (customizer != null) { + customizers.add(customizer); + } + } + return customizers; + } + /** - * @since 4.3 + * Get the default {@link ContextCustomizerFactory} instances for this bootstrapper. */ + protected List geContextCustomizerFactories() { + return SpringFactoriesLoader.loadFactories(ContextCustomizerFactory.class, + getClass().getClassLoader()); + } + private boolean areAllEmpty(Collection... collections) { for (Collection collection : collections) { if (!collection.isEmpty()) { diff --git a/spring-test/src/main/java/org/springframework/test/context/web/AbstractGenericWebContextLoader.java b/spring-test/src/main/java/org/springframework/test/context/web/AbstractGenericWebContextLoader.java index f6e1ac9e68..53a3b7250f 100644 --- a/spring-test/src/main/java/org/springframework/test/context/web/AbstractGenericWebContextLoader.java +++ b/spring-test/src/main/java/org/springframework/test/context/web/AbstractGenericWebContextLoader.java @@ -256,15 +256,13 @@ public abstract class AbstractGenericWebContextLoader extends AbstractContextLoa * loader after bean definitions have been loaded into the context but * before the context is refreshed. * - *

    The default implementation is empty but can be overridden in subclasses - * to customize the web application context. - * * @param context the newly created web application context * @param webMergedConfig the merged context configuration to use to load the * web application context * @see #loadContext(MergedContextConfiguration) */ protected void customizeContext(GenericWebApplicationContext context, WebMergedContextConfiguration webMergedConfig) { + super.customizeContext(context, webMergedConfig); } // --- ContextLoader ------------------------------------------------------- diff --git a/spring-test/src/test/java/org/springframework/test/context/MergedContextConfigurationTests.java b/spring-test/src/test/java/org/springframework/test/context/MergedContextConfigurationTests.java index bf8eba0403..733f7c0c3b 100644 --- a/spring-test/src/test/java/org/springframework/test/context/MergedContextConfigurationTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/MergedContextConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -16,6 +16,7 @@ package org.springframework.test.context; +import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -28,6 +29,7 @@ import org.springframework.test.context.support.AnnotationConfigContextLoader; import org.springframework.test.context.support.GenericXmlContextLoader; import static org.junit.Assert.*; +import static org.mockito.Mockito.*; /** * Unit tests for {@link MergedContextConfiguration}. @@ -400,6 +402,36 @@ public class MergedContextConfigurationTests { assertNotEquals(mergedConfig2, mergedConfig1); } + @Test + public void equalsWithSameContextCustomizers() { + Set customizers1 = Collections.singleton( + mock(ContextCustomizer.class)); + MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration( + getClass(), EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, null, + EMPTY_STRING_ARRAY, null, null, customizers1, loader, null, null); + MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration( + getClass(), EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, null, + EMPTY_STRING_ARRAY, null, null, customizers1, loader, null, null); + assertEquals(mergedConfig1, mergedConfig2); + } + + @Test + public void equalsWithDifferentContextCustomizers() { + Set customizers1 = Collections.singleton( + mock(ContextCustomizer.class)); + Set customizers2 = Collections.singleton( + mock(ContextCustomizer.class)); + + MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration( + getClass(), EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, null, + EMPTY_STRING_ARRAY, null, null, customizers1, loader, null, null); + MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration( + getClass(), EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY, null, + EMPTY_STRING_ARRAY, null, null, customizers2, loader, null, null); + assertNotEquals(mergedConfig1, mergedConfig2); + assertNotEquals(mergedConfig2, mergedConfig1); + } + /** * @since 3.2.2 */ diff --git a/spring-test/src/test/java/org/springframework/test/context/TestContextManagerVerifyAttributesTests.java b/spring-test/src/test/java/org/springframework/test/context/TestContextManagerVerifyAttributesTests.java new file mode 100644 index 0000000000..72d83a38c6 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/TestContextManagerVerifyAttributesTests.java @@ -0,0 +1,71 @@ +/* + * Copyright 2002-2016 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 + * + * http://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; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; + +import static org.hamcrest.CoreMatchers.*; + +/** + * JUnit 4 based unit test for {@link TestContextManager}, which verifies + * ContextConfiguration attributes are defined. + * + * @author Phillip Webb + * @since 4.3 + */ +public class TestContextManagerVerifyAttributesTests { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void processContextConfigurationWithMissingContextConfigAttributes() { + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage(containsString("was unable to detect defaults, " + + "and no ApplicationContextInitializers or ContextCustomizers were " + + "declared for context configuration")); + new TestContextManager(MissingContextAttributes.class); + } + + @Test + public void processContextConfigurationWitListener() { + new TestContextManager(WithInitializer.class); + } + + + @ContextConfiguration + private static class MissingContextAttributes { + + } + + @ContextConfiguration(initializers=ExampleInitializer.class) + private static class WithInitializer { + + } + + static class ExampleInitializer implements ApplicationContextInitializer { + + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + } + + } +} diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/ContextCustomizerSpringRunnerTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/ContextCustomizerSpringRunnerTests.java new file mode 100644 index 0000000000..c978c4977d --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/ContextCustomizerSpringRunnerTests.java @@ -0,0 +1,88 @@ +/* + * Copyright 2002-2016 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 + * + * http://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.junit4; + +import java.util.Collections; +import java.util.List; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.test.context.BootstrapWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.ContextConfigurationAttributes; +import org.springframework.test.context.ContextCustomizer; +import org.springframework.test.context.ContextCustomizerFactory; +import org.springframework.test.context.MergedContextConfiguration; +import org.springframework.test.context.junit4.ContextCustomizerSpringRunnerTests.CustomTestContextBootstrapper; +import org.springframework.test.context.support.DefaultTestContextBootstrapper; + +import static org.junit.Assert.*; + +/** + * JUnit 4 based integration test which verifies support of + * {@link ContextCustomizerFactory} and {@link ContextCustomizer}. + * + * @author Phillip Webb + * @since 4.3 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@BootstrapWith(CustomTestContextBootstrapper.class) +@ContextConfiguration +public class ContextCustomizerSpringRunnerTests { + + @Autowired + private MyBean myBean; + + @Test + public void injectedMyBean() throws Exception { + assertNotNull(this.myBean); + } + + public static class CustomTestContextBootstrapper + extends DefaultTestContextBootstrapper { + + @Override + protected List geContextCustomizerFactories() { + return Collections.singletonList(new ContextCustomizerFactory() { + + @Override + public ContextCustomizer getContextCustomizer(Class testClass, + List configurationAttributes) { + return new TestContextCustomizers(); + } + + }); + } + + } + + public static class TestContextCustomizers implements ContextCustomizer { + + @Override + public void customizeContext(ConfigurableApplicationContext context, + MergedContextConfiguration mergedContextConfiguration) { + context.getBeanFactory().registerSingleton("mybean", new MyBean()); + } + + } + + public static class MyBean { + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/support/BootstrapTestUtilsMergedConfigTests.java b/spring-test/src/test/java/org/springframework/test/context/support/BootstrapTestUtilsMergedConfigTests.java index 44a780d23f..1b509fc92e 100644 --- a/spring-test/src/test/java/org/springframework/test/context/support/BootstrapTestUtilsMergedConfigTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/support/BootstrapTestUtilsMergedConfigTests.java @@ -63,7 +63,7 @@ public class BootstrapTestUtilsMergedConfigTests extends AbstractContextConfigur public void buildMergedConfigWithContextConfigurationWithoutLocationsClassesOrInitializers() { exception.expect(IllegalStateException.class); exception.expectMessage(startsWith("DelegatingSmartContextLoader was unable to detect defaults, " - + "and no ApplicationContextInitializers were declared for context configuration attributes")); + + "and no ApplicationContextInitializers or ContextCustomizers were declared for context configuration attribute")); buildMergedContextConfiguration(MissingContextAttributesTestCase.class); }