Support declarative ContextCustomizerFactory registration in the TCF
Prior to this commit, it was only possible to register a ContextCustomizerFactory in the TestContext framework (TCF) via the SpringFactoriesLoader mechanism. This commit introduces support for declarative registration of a ContextCustomizerFactory local to a test class via a new @ContextCustomizerFactories annotation. Closes gh-26148
This commit is contained in:
@@ -34,6 +34,7 @@ import org.springframework.context.ConfigurableApplicationContext;
|
||||
* @author Sam Brannen
|
||||
* @since 4.3
|
||||
* @see ContextCustomizerFactory
|
||||
* @see ContextCustomizerFactories @ContextCustomizerFactories
|
||||
* @see org.springframework.test.context.support.AbstractContextLoader#customizeContext
|
||||
*/
|
||||
@FunctionalInterface
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
/**
|
||||
* {@code @ContextCustomizerFactories} defines class-level metadata for configuring
|
||||
* which {@link ContextCustomizerFactory} implementations should be registered with
|
||||
* the <em>Spring TestContext Framework</em>.
|
||||
*
|
||||
* <p>{@code @ContextCustomizerFactories} is used to register factories for a
|
||||
* particular test class, its subclasses, and its nested classes. If you wish to
|
||||
* register a factory globally, you should register it via the automatic discovery
|
||||
* mechanism described in {@link ContextCustomizerFactory}.
|
||||
*
|
||||
* <p>This annotation may be used as a <em>meta-annotation</em> to create custom
|
||||
* <em>composed annotations</em>. In addition, this annotation will be inherited
|
||||
* from an enclosing test class by default. See
|
||||
* {@link NestedTestConfiguration @NestedTestConfiguration} for details.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 6.1
|
||||
* @see ContextCustomizerFactory
|
||||
* @see ContextCustomizer
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
public @interface ContextCustomizerFactories {
|
||||
|
||||
/**
|
||||
* Alias for {@link #factories}.
|
||||
* <p>This attribute may <strong>not</strong> be used in conjunction with
|
||||
* {@link #factories}, but it may be used instead of {@link #factories}.
|
||||
*/
|
||||
@AliasFor("factories")
|
||||
Class<? extends ContextCustomizerFactory>[] value() default {};
|
||||
|
||||
/**
|
||||
* The {@link ContextCustomizerFactory} implementations to register.
|
||||
* <p>This attribute may <strong>not</strong> be used in conjunction with
|
||||
* {@link #value}, but it may be used instead of {@link #value}.
|
||||
*/
|
||||
@AliasFor("value")
|
||||
Class<? extends ContextCustomizerFactory>[] factories() default {};
|
||||
|
||||
/**
|
||||
* Whether the configured set of {@link #factories} from superclasses and
|
||||
* enclosing classes should be <em>inherited</em>.
|
||||
* <p>The default value is {@code true}, which means that an annotated class
|
||||
* will <em>inherit</em> the factories defined by an annotated superclass or
|
||||
* enclosing class. Specifically, the factories for an annotated class will be
|
||||
* appended to the list of factories defined by an annotated superclass or
|
||||
* enclosing class. Thus, subclasses and nested classes have the option of
|
||||
* <em>extending</em> the list of factories.
|
||||
* <p>If {@code inheritListeners} is set to {@code false}, the factories for
|
||||
* the annotated class will <em>shadow</em> and effectively replace any
|
||||
* factories defined by a superclass or enclosing class.
|
||||
*/
|
||||
boolean inheritFactories() default true;
|
||||
|
||||
/**
|
||||
* The <em>merge mode</em> to use when {@code @ContextCustomizerFactories} is
|
||||
* declared on a class that does <strong>not</strong> inherit factories from
|
||||
* a superclass or enclosing class.
|
||||
* <p>Can be set to {@link MergeMode#REPLACE_DEFAULTS REPLACE_DEFAULTS} to
|
||||
* have locally declared factories replace the default factories.
|
||||
* <p>The mode is ignored if factories are inherited from a superclass or
|
||||
* enclosing class.
|
||||
* <p>Defaults to {@link MergeMode#MERGE_WITH_DEFAULTS MERGE_WITH_DEFAULTS}.
|
||||
* @see MergeMode
|
||||
*/
|
||||
MergeMode mergeMode() default MergeMode.MERGE_WITH_DEFAULTS;
|
||||
|
||||
|
||||
/**
|
||||
* Enumeration of <em>modes</em> that dictate whether explicitly declared
|
||||
* factories are merged with the default factories when
|
||||
* {@code @ContextCustomizerFactories} is declared on a class that does
|
||||
* <strong>not</strong> inherit factories from a superclass or enclosing
|
||||
* class.
|
||||
*/
|
||||
enum MergeMode {
|
||||
|
||||
/**
|
||||
* Indicates that locally declared factories should be merged with the
|
||||
* default factories.
|
||||
* <p>The merging algorithm ensures that duplicates are removed from the
|
||||
* list and that locally declared factories are appended to the list of
|
||||
* default factories when merged.
|
||||
*/
|
||||
MERGE_WITH_DEFAULTS,
|
||||
|
||||
/**
|
||||
* Indicates that locally declared factories should replace the default
|
||||
* factories.
|
||||
*/
|
||||
REPLACE_DEFAULTS
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -29,12 +29,18 @@ import org.springframework.lang.Nullable;
|
||||
*
|
||||
* <p>By default, the Spring TestContext Framework will use the
|
||||
* {@link org.springframework.core.io.support.SpringFactoriesLoader SpringFactoriesLoader}
|
||||
* mechanism for loading factories configured in all {@code META-INF/spring.factories}
|
||||
* mechanism for loading default factories configured in all {@code META-INF/spring.factories}
|
||||
* files on the classpath.
|
||||
*
|
||||
* <p>As of Spring Framework 6.1, it is also possible to register factories
|
||||
* declaratively via the {@link ContextCustomizerFactories @ContextCustomizerFactories}
|
||||
* annotation.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
* @since 4.3
|
||||
* @see ContextCustomizer
|
||||
* @see ContextCustomizerFactories @ContextCustomizerFactories
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ContextCustomizerFactory {
|
||||
|
||||
@@ -37,6 +37,7 @@ 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.ContextCustomizerFactories;
|
||||
import org.springframework.test.context.ContextCustomizerFactory;
|
||||
import org.springframework.test.context.ContextHierarchy;
|
||||
import org.springframework.test.context.ContextLoader;
|
||||
@@ -378,7 +379,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
||||
private Set<ContextCustomizer> getContextCustomizers(Class<?> testClass,
|
||||
List<ContextConfigurationAttributes> configAttributes) {
|
||||
|
||||
List<ContextCustomizerFactory> factories = getContextCustomizerFactories();
|
||||
List<ContextCustomizerFactory> factories = getContextCustomizerFactories(testClass);
|
||||
Set<ContextCustomizer> customizers = new LinkedHashSet<>(factories.size());
|
||||
for (ContextCustomizerFactory factory : factories) {
|
||||
ContextCustomizer customizer = factory.createContextCustomizer(testClass, configAttributes);
|
||||
@@ -397,6 +398,69 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
||||
return customizers;
|
||||
}
|
||||
|
||||
private List<ContextCustomizerFactory> getContextCustomizerFactories(Class<?> testClass) {
|
||||
AnnotationDescriptor<ContextCustomizerFactories> descriptor =
|
||||
TestContextAnnotationUtils.findAnnotationDescriptor(testClass, ContextCustomizerFactories.class);
|
||||
List<ContextCustomizerFactory> factories = new ArrayList<>();
|
||||
|
||||
if (descriptor == null) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("@ContextCustomizerFactories is not present for class [%s]"
|
||||
.formatted(testClass.getName()));
|
||||
}
|
||||
factories.addAll(getContextCustomizerFactories());
|
||||
}
|
||||
else {
|
||||
// Traverse the class hierarchy...
|
||||
while (descriptor != null) {
|
||||
Class<?> declaringClass = descriptor.getDeclaringClass();
|
||||
ContextCustomizerFactories annotation = descriptor.getAnnotation();
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Retrieved %s for declaring class [%s]."
|
||||
.formatted(annotation, declaringClass.getName()));
|
||||
}
|
||||
|
||||
boolean inheritFactories = annotation.inheritFactories();
|
||||
AnnotationDescriptor<ContextCustomizerFactories> parentDescriptor = descriptor.next();
|
||||
factories.addAll(0, instantiateCustomizerFactories(annotation.factories()));
|
||||
|
||||
// If there are no factories to inherit, we might need to merge the
|
||||
// locally declared factories with the defaults.
|
||||
if ((!inheritFactories || parentDescriptor == null) &&
|
||||
annotation.mergeMode() == ContextCustomizerFactories.MergeMode.MERGE_WITH_DEFAULTS) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("Merging default factories with factories configured via " +
|
||||
"@ContextCustomizerFactories for class [%s].", descriptor.getRootDeclaringClass().getName()));
|
||||
}
|
||||
factories.addAll(0, getContextCustomizerFactories());
|
||||
}
|
||||
|
||||
descriptor = (inheritFactories ? parentDescriptor : null);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove possible duplicates.
|
||||
List<ContextCustomizerFactory> uniqueFactories = new ArrayList<>(factories.size());
|
||||
factories.forEach(factory -> {
|
||||
Class<? extends ContextCustomizerFactory> factoryClass = factory.getClass();
|
||||
if (uniqueFactories.stream().map(Object::getClass).noneMatch(factoryClass::equals)) {
|
||||
uniqueFactories.add(factory);
|
||||
}
|
||||
});
|
||||
factories = uniqueFactories;
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Using ContextCustomizerFactory implementations for test class [%s]: %s"
|
||||
.formatted(testClass.getName(), factories));
|
||||
}
|
||||
else if (logger.isDebugEnabled()) {
|
||||
logger.debug("Using ContextCustomizerFactory implementations for test class [%s]: %s"
|
||||
.formatted(testClass.getSimpleName(), classSimpleNames(factories)));
|
||||
}
|
||||
|
||||
return factories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link ContextCustomizerFactory} instances for this bootstrapper.
|
||||
* <p>The default implementation delegates to
|
||||
@@ -407,6 +471,33 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
|
||||
return TestContextSpringFactoriesUtils.loadFactoryImplementations(ContextCustomizerFactory.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<ContextCustomizerFactory> instantiateCustomizerFactories(Class<? extends ContextCustomizerFactory>... classes) {
|
||||
List<ContextCustomizerFactory> factories = new ArrayList<>(classes.length);
|
||||
for (Class<? extends ContextCustomizerFactory> factoryClass : classes) {
|
||||
try {
|
||||
factories.add(BeanUtils.instantiateClass(factoryClass));
|
||||
}
|
||||
catch (BeanInstantiationException ex) {
|
||||
Throwable cause = ex.getCause();
|
||||
if (cause instanceof ClassNotFoundException || cause instanceof NoClassDefFoundError) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("""
|
||||
Skipping candidate %1$s [%2$s] due to a missing dependency. \
|
||||
Specify custom %1$s classes or make the default %1$s classes \
|
||||
and their required dependencies available. Offending class: [%3$s]"""
|
||||
.formatted(ContextCustomizerFactory.class.getSimpleName(), factoryClass.getName(),
|
||||
cause.getMessage()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
return factories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the {@link ContextLoader} {@linkplain Class class} to use for the
|
||||
* supplied list of {@link ContextConfigurationAttributes} and then instantiate
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.customizers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.ContextCustomizerFactory;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 6.1
|
||||
*/
|
||||
class BarContextCustomizerFactory implements ContextCustomizerFactory {
|
||||
|
||||
@Override
|
||||
public ContextCustomizer createContextCustomizer(Class<?> testClass,
|
||||
List<ContextConfigurationAttributes> configAttributes) {
|
||||
return (context, mergedConfig) -> context.getBeanFactory().registerSingleton("bar", "baz");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.customizers;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.ContextCustomizerFactories;
|
||||
import org.springframework.test.context.ContextCustomizerFactory;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Integration test which verifies support for {@link ContextCustomizerFactory}
|
||||
* and {@link ContextCustomizer} when a custom factory is registered declaratively
|
||||
* via {@link ContextCustomizerFactories @ContextCustomizerFactories}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 6.1
|
||||
*/
|
||||
@SpringJUnitConfig({})
|
||||
@CustomizeWithFruit
|
||||
@CustomizeWithFoo
|
||||
@CustomizeWithBar
|
||||
class ContextCustomizerDeclarativeRegistrationComposedAnnotationTests {
|
||||
|
||||
// GlobalFruitContextCustomizerFactory is registered via spring.factories
|
||||
@Autowired(required = false)
|
||||
@Qualifier("global$fruit")
|
||||
String fruit;
|
||||
|
||||
@Autowired(required = false)
|
||||
@Qualifier("foo")
|
||||
String foo;
|
||||
|
||||
@Autowired(required = false)
|
||||
@Qualifier("bar")
|
||||
String bar;
|
||||
|
||||
|
||||
@Test
|
||||
void injectedBean() {
|
||||
// registered globally via spring.factories
|
||||
assertThat(fruit).isEqualTo("apple, banana, cherry");
|
||||
|
||||
// From local @ContextCustomizerFactories
|
||||
assertThat(foo).isEqualTo("bar");
|
||||
|
||||
// @ContextCustomizerFactories is not currently supported as a repeatable annotation.
|
||||
assertThat(bar).isNull();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.customizers;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.ContextCustomizerFactories;
|
||||
import org.springframework.test.context.ContextCustomizerFactory;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Integration test which verifies support for {@link ContextCustomizerFactory}
|
||||
* and {@link ContextCustomizer} when a custom factory is registered declaratively
|
||||
* via {@link ContextCustomizerFactories @ContextCustomizerFactories}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 6.1
|
||||
*/
|
||||
@SpringJUnitConfig({})
|
||||
@CustomizeWithFruit
|
||||
@CustomizeWithFoo
|
||||
@ContextCustomizerFactories(EnigmaContextCustomizerFactory.class)
|
||||
@CustomizeWithBar
|
||||
class ContextCustomizerDeclarativeRegistrationTests {
|
||||
|
||||
// GlobalFruitContextCustomizerFactory is registered via spring.factories
|
||||
@Autowired(required = false)
|
||||
@Qualifier("global$fruit")
|
||||
String fruit;
|
||||
|
||||
@Autowired
|
||||
Integer enigma;
|
||||
|
||||
@Autowired(required = false)
|
||||
@Qualifier("foo")
|
||||
String foo;
|
||||
|
||||
@Autowired(required = false)
|
||||
@Qualifier("bar")
|
||||
String bar;
|
||||
|
||||
|
||||
@Test
|
||||
void injectedBean() {
|
||||
// registered globally via spring.factories
|
||||
assertThat(fruit).isEqualTo("apple, banana, cherry");
|
||||
|
||||
// From local @ContextCustomizerFactories
|
||||
assertThat(enigma).isEqualTo(42);
|
||||
|
||||
// @ContextCustomizerFactories is not currently supported as a repeatable annotation,
|
||||
// and a directly present @ContextCustomizerFactories annotation overrides
|
||||
// @ContextCustomizerFactories meta-annotations.
|
||||
assertThat(foo).isNull();
|
||||
assertThat(bar).isNull();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.customizers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.test.context.BootstrapWith;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.ContextCustomizerFactories;
|
||||
import org.springframework.test.context.ContextCustomizerFactory;
|
||||
import org.springframework.test.context.customizers.ContextCustomizerTests.EnigmaTestContextBootstrapper;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
import org.springframework.test.context.support.AbstractTestContextBootstrapper;
|
||||
import org.springframework.test.context.support.DefaultTestContextBootstrapper;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Integration test which verifies support for {@link ContextCustomizerFactory}
|
||||
* and {@link ContextCustomizer} when a custom factory is registered by overriding
|
||||
* {@link AbstractTestContextBootstrapper#getContextCustomizerFactories} and
|
||||
* additional factories are registered declaratively via
|
||||
* {@link ContextCustomizerFactories @ContextCustomizerFactories}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Phillip Webb
|
||||
* @since 4.3
|
||||
*/
|
||||
@SpringJUnitConfig({})
|
||||
@CustomizeWithFoo
|
||||
@BootstrapWith(EnigmaTestContextBootstrapper.class)
|
||||
@CustomizeWithBar
|
||||
class ContextCustomizerTests {
|
||||
|
||||
// GlobalFruitContextCustomizerFactory is registered via spring.factories
|
||||
@Autowired(required = false)
|
||||
@Qualifier("global$fruit")
|
||||
String fruit;
|
||||
|
||||
@Autowired
|
||||
Integer enigma;
|
||||
|
||||
@Autowired(required = false)
|
||||
@Qualifier("foo")
|
||||
String foo;
|
||||
|
||||
@Autowired(required = false)
|
||||
@Qualifier("bar")
|
||||
String bar;
|
||||
|
||||
|
||||
@Test
|
||||
void injectedBean() {
|
||||
// Local Bootstrapper overrides spring.factories lookup
|
||||
assertThat(fruit).isNull();
|
||||
|
||||
// From local Bootstrapper
|
||||
assertThat(enigma).isEqualTo(42);
|
||||
|
||||
// From local @ContextCustomizerFactories
|
||||
assertThat(foo).isEqualTo("bar");
|
||||
|
||||
// @ContextCustomizerFactories is not currently supported as a repeatable annotation.
|
||||
assertThat(bar).isNull();
|
||||
}
|
||||
|
||||
|
||||
static class EnigmaTestContextBootstrapper extends DefaultTestContextBootstrapper {
|
||||
|
||||
@Override
|
||||
protected List<ContextCustomizerFactory> getContextCustomizerFactories() {
|
||||
return List.of(new EnigmaContextCustomizerFactory());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.customizers;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.test.context.ContextCustomizerFactories;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 6.1
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@ContextCustomizerFactories(BarContextCustomizerFactory.class)
|
||||
@interface CustomizeWithBar {
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.customizers;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.test.context.ContextCustomizerFactories;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 6.1
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@ContextCustomizerFactories(FooContextCustomizerFactory.class)
|
||||
@interface CustomizeWithFoo {
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.customizers;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Marker annotation to trigger {@link GlobalFruitContextCustomizerFactory}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 6.1
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Inherited
|
||||
@interface CustomizeWithFruit {
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.customizers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.ContextCustomizerFactory;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 6.1
|
||||
*/
|
||||
class EnigmaContextCustomizerFactory implements ContextCustomizerFactory {
|
||||
|
||||
@Override
|
||||
public ContextCustomizer createContextCustomizer(Class<?> testClass,
|
||||
List<ContextConfigurationAttributes> configAttributes) {
|
||||
return (context, mergedConfig) -> context.getBeanFactory().registerSingleton("enigma", 42);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.customizers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.ContextCustomizerFactory;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 6.1
|
||||
*/
|
||||
class FooContextCustomizerFactory implements ContextCustomizerFactory {
|
||||
|
||||
@Override
|
||||
public ContextCustomizer createContextCustomizer(Class<?> testClass,
|
||||
List<ContextConfigurationAttributes> configAttributes) {
|
||||
return (context, mergedConfig) -> context.getBeanFactory().registerSingleton("foo", "bar");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.customizers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.ContextCustomizerFactory;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 6.1
|
||||
*/
|
||||
class GlobalFruitContextCustomizerFactory implements ContextCustomizerFactory {
|
||||
|
||||
@Override
|
||||
public ContextCustomizer createContextCustomizer(Class<?> testClass,
|
||||
List<ContextConfigurationAttributes> configAttributes) {
|
||||
|
||||
if (testClass.isAnnotationPresent(CustomizeWithFruit.class)) {
|
||||
return new GlobalFruitContextCustomizer();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class GlobalFruitContextCustomizer implements ContextCustomizer {
|
||||
|
||||
@Override
|
||||
public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
|
||||
// Use "global$fruit" as the bean name instead of something simple like "fruit"
|
||||
// to avoid bean name clashes with any test that registers a bean named "fruit".
|
||||
context.getBeanFactory().registerSingleton("global$fruit", "apple, banana, cherry");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object other) {
|
||||
return (this == other || (other != null && getClass() == other.getClass()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getClass().hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.customizers;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 6.1
|
||||
*/
|
||||
@CustomizeWithBar
|
||||
class InheritedContextCustomizerRegistrationTests extends LocalContextCustomizerRegistrationTests {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("bar")
|
||||
String bar;
|
||||
|
||||
|
||||
@Override
|
||||
@Test
|
||||
void injectedBean() {
|
||||
assertThat(fruit).isEqualTo("apple, banana, cherry");
|
||||
assertThat(foo).isEqualTo("bar");
|
||||
assertThat(bar).isEqualTo("baz");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.customizers;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 6.1
|
||||
*/
|
||||
@SpringJUnitConfig({})
|
||||
@CustomizeWithFruit
|
||||
@CustomizeWithFoo
|
||||
class LocalContextCustomizerRegistrationTests {
|
||||
|
||||
// GlobalFruitContextCustomizerFactory is registered via spring.factories
|
||||
@Autowired(required = false)
|
||||
@Qualifier("global$fruit")
|
||||
String fruit;
|
||||
|
||||
@Autowired(required = false)
|
||||
@Qualifier("foo")
|
||||
String foo;
|
||||
|
||||
|
||||
@Test
|
||||
void injectedBean() {
|
||||
assertThat(fruit).isEqualTo("apple, banana, cherry");
|
||||
assertThat(foo).isEqualTo("bar");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.customizers;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.test.context.ContextCustomizerFactories;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 6.1
|
||||
*/
|
||||
@ContextCustomizerFactories(factories = {BarContextCustomizerFactory.class, EnigmaContextCustomizerFactory.class},
|
||||
inheritFactories = false)
|
||||
class OverriddenContextCustomizerRegistrationTests extends LocalContextCustomizerRegistrationTests {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("bar")
|
||||
String bar;
|
||||
|
||||
@Autowired
|
||||
Integer enigma;
|
||||
|
||||
|
||||
@Override
|
||||
@Test
|
||||
void injectedBean() {
|
||||
// globally registered via spring.factories
|
||||
assertThat(fruit).isEqualTo("apple, banana, cherry");
|
||||
|
||||
// Overridden by this subclass (inheritFactories = false)
|
||||
assertThat(foo).isNull();
|
||||
|
||||
// Local to this subclass
|
||||
assertThat(bar).isEqualTo("baz");
|
||||
assertThat(enigma).isEqualTo(42);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.customizers;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.test.context.ContextCustomizerFactories;
|
||||
import org.springframework.test.context.ContextCustomizerFactories.MergeMode;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 6.1
|
||||
*/
|
||||
@SpringJUnitConfig({})
|
||||
@ContextCustomizerFactories(factories = {FooContextCustomizerFactory.class, BarContextCustomizerFactory.class},
|
||||
mergeMode = MergeMode.REPLACE_DEFAULTS)
|
||||
class ReplaceDefaultsContextCustomizerTests {
|
||||
|
||||
// GlobalFruitContextCustomizerFactory is registered via spring.factories
|
||||
@Autowired(required = false)
|
||||
@Qualifier("global$fruit")
|
||||
String fruit;
|
||||
|
||||
@Autowired(required = false)
|
||||
@Qualifier("foo")
|
||||
String foo;
|
||||
|
||||
@Autowired(required = false)
|
||||
@Qualifier("bar")
|
||||
String bar;
|
||||
|
||||
|
||||
@Test
|
||||
void injectedBean() {
|
||||
// MergeMode.REPLACE_DEFAULTS overrides spring.factories lookup
|
||||
assertThat(fruit).isNull();
|
||||
|
||||
// From local @ContextCustomizerFactories
|
||||
assertThat(foo).isEqualTo("bar");
|
||||
assertThat(bar).isEqualTo("baz");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2019 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.junit4;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.BootstrapWith;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.ContextCustomizerFactory;
|
||||
import org.springframework.test.context.junit4.ContextCustomizerSpringRunnerTests.CustomTestContextBootstrapper;
|
||||
import org.springframework.test.context.support.DefaultTestContextBootstrapper;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* JUnit 4 based integration test which verifies support of
|
||||
* {@link ContextCustomizerFactory} and {@link ContextCustomizer}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @author Phillip Webb
|
||||
* @since 4.3
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@BootstrapWith(CustomTestContextBootstrapper.class)
|
||||
public class ContextCustomizerSpringRunnerTests {
|
||||
|
||||
@Autowired String foo;
|
||||
|
||||
|
||||
@Test
|
||||
public void injectedBean() {
|
||||
assertThat(foo).isEqualTo("foo");
|
||||
}
|
||||
|
||||
|
||||
static class CustomTestContextBootstrapper extends DefaultTestContextBootstrapper {
|
||||
|
||||
@Override
|
||||
protected List<ContextCustomizerFactory> getContextCustomizerFactories() {
|
||||
return singletonList(
|
||||
(ContextCustomizerFactory) (testClass, configAttributes) ->
|
||||
(ContextCustomizer) (context, mergedConfig) -> context.getBeanFactory().registerSingleton("foo", "foo")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,7 +4,8 @@
|
||||
org.springframework.test.context.TestExecutionListener = org.example.FooListener
|
||||
|
||||
org.springframework.test.context.ContextCustomizerFactory =\
|
||||
org.springframework.test.context.aot.samples.basic.ImportsContextCustomizerFactory
|
||||
org.springframework.test.context.aot.samples.basic.ImportsContextCustomizerFactory,\
|
||||
org.springframework.test.context.customizers.GlobalFruitContextCustomizerFactory
|
||||
|
||||
org.springframework.test.context.ApplicationContextFailureProcessor =\
|
||||
org.springframework.test.context.failures.TrackingApplicationContextFailureProcessor
|
||||
|
||||
Reference in New Issue
Block a user