Browse Source

Polishing

pull/976/merge
Juergen Hoeller 10 years ago
parent
commit
d2d528dc05
  1. 64
      spring-test/src/main/java/org/springframework/test/context/BootstrapUtils.java
  2. 10
      spring-test/src/main/java/org/springframework/test/context/TestExecutionListener.java
  3. 86
      spring-test/src/main/java/org/springframework/test/context/TestExecutionListeners.java
  4. 30
      spring-test/src/main/java/org/springframework/test/context/TestPropertySource.java
  5. 76
      spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java
  6. 21
      spring-test/src/main/java/org/springframework/test/util/MetaAnnotationUtils.java
  7. 90
      spring-test/src/test/java/org/springframework/test/context/TestExecutionListenersTests.java

64
spring-test/src/main/java/org/springframework/test/context/BootstrapUtils.java

@ -43,51 +43,47 @@ import static org.springframework.beans.BeanUtils.instantiateClass; @@ -43,51 +43,47 @@ import static org.springframework.beans.BeanUtils.instantiateClass;
*/
abstract class BootstrapUtils {
private static final String DEFAULT_BOOTSTRAP_CONTEXT_CLASS_NAME = "org.springframework.test.context.support.DefaultBootstrapContext";
private static final String DEFAULT_BOOTSTRAP_CONTEXT_CLASS_NAME =
"org.springframework.test.context.support.DefaultBootstrapContext";
private static final String DEFAULT_CACHE_AWARE_CONTEXT_LOADER_DELEGATE_CLASS_NAME = "org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate";
private static final String DEFAULT_CACHE_AWARE_CONTEXT_LOADER_DELEGATE_CLASS_NAME =
"org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate";
private static final String DEFAULT_TEST_CONTEXT_BOOTSTRAPPER_CLASS_NAME = "org.springframework.test.context.support.DefaultTestContextBootstrapper";
private static final String DEFAULT_TEST_CONTEXT_BOOTSTRAPPER_CLASS_NAME =
"org.springframework.test.context.support.DefaultTestContextBootstrapper";
private static final String DEFAULT_WEB_TEST_CONTEXT_BOOTSTRAPPER_CLASS_NAME = "org.springframework.test.context.web.WebTestContextBootstrapper";
private static final String DEFAULT_WEB_TEST_CONTEXT_BOOTSTRAPPER_CLASS_NAME =
"org.springframework.test.context.web.WebTestContextBootstrapper";
private static final String WEB_APP_CONFIGURATION_ANNOTATION_CLASS_NAME = "org.springframework.test.context.web.WebAppConfiguration";
private static final String WEB_APP_CONFIGURATION_ANNOTATION_CLASS_NAME =
"org.springframework.test.context.web.WebAppConfiguration";
private static final Log logger = LogFactory.getLog(BootstrapUtils.class);
private BootstrapUtils() {
/* no-op */
}
/**
* Create the {@code BootstrapContext} for the specified {@linkplain Class test class}.
*
* <p>Uses reflection to create a {@link org.springframework.test.context.support.DefaultBootstrapContext}
* that uses a {@link org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate}.
*
* @param testClass the test class for which the bootstrap context should be created
* @return a new {@code BootstrapContext}; never {@code null}
*/
@SuppressWarnings("unchecked")
static BootstrapContext createBootstrapContext(Class<?> testClass) {
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate = createCacheAwareContextLoaderDelegate();
Class<? extends BootstrapContext> clazz = null;
try {
clazz = (Class<? extends BootstrapContext>) ClassUtils.forName(DEFAULT_BOOTSTRAP_CONTEXT_CLASS_NAME,
BootstrapUtils.class.getClassLoader());
Constructor<? extends BootstrapContext> constructor = clazz.getConstructor(Class.class,
CacheAwareContextLoaderDelegate.class);
clazz = (Class<? extends BootstrapContext>) ClassUtils.forName(
DEFAULT_BOOTSTRAP_CONTEXT_CLASS_NAME, BootstrapUtils.class.getClassLoader());
Constructor<? extends BootstrapContext> constructor = clazz.getConstructor(
Class.class, CacheAwareContextLoaderDelegate.class);
if (logger.isDebugEnabled()) {
logger.debug(String.format("Instantiating BootstrapContext using constructor [%s]", constructor));
}
return instantiateClass(constructor, testClass, cacheAwareContextLoaderDelegate);
}
catch (Throwable t) {
throw new IllegalStateException("Could not load BootstrapContext [" + clazz + "]", t);
catch (Throwable ex) {
throw new IllegalStateException("Could not load BootstrapContext [" + clazz + "]", ex);
}
}
@ -104,8 +100,8 @@ abstract class BootstrapUtils { @@ -104,8 +100,8 @@ abstract class BootstrapUtils {
}
return instantiateClass(clazz, CacheAwareContextLoaderDelegate.class);
}
catch (Throwable t) {
throw new IllegalStateException("Could not load CacheAwareContextLoaderDelegate [" + clazz + "]", t);
catch (Throwable ex) {
throw new IllegalStateException("Could not load CacheAwareContextLoaderDelegate [" + clazz + "]", ex);
}
}
@ -113,7 +109,6 @@ abstract class BootstrapUtils { @@ -113,7 +109,6 @@ abstract class BootstrapUtils {
* Resolve the {@link TestContextBootstrapper} type for the test class in the
* supplied {@link BootstrapContext}, instantiate it, and provide it a reference
* to the {@link BootstrapContext}.
*
* <p>If the {@link BootstrapWith @BootstrapWith} annotation is present on
* the test class, either directly or as a meta-annotation, then its
* {@link BootstrapWith#value value} will be used as the bootstrapper type.
@ -123,7 +118,6 @@ abstract class BootstrapUtils { @@ -123,7 +118,6 @@ abstract class BootstrapUtils {
* {@link org.springframework.test.context.web.WebTestContextBootstrapper
* WebTestContextBootstrapper} will be used, depending on the presence of
* {@link org.springframework.test.context.web.WebAppConfiguration @WebAppConfiguration}.
*
* @param bootstrapContext the bootstrap context to use
* @return a fully configured {@code TestContextBootstrapper}
*/
@ -138,21 +132,19 @@ abstract class BootstrapUtils { @@ -138,21 +132,19 @@ abstract class BootstrapUtils {
}
if (logger.isDebugEnabled()) {
logger.debug(String.format("Instantiating TestContextBootstrapper for test class [%s] from class [%s]",
testClass.getName(), clazz.getName()));
testClass.getName(), clazz.getName()));
}
TestContextBootstrapper testContextBootstrapper = instantiateClass(clazz, TestContextBootstrapper.class);
testContextBootstrapper.setBootstrapContext(bootstrapContext);
return testContextBootstrapper;
}
catch (Throwable ex) {
if (ex instanceof IllegalStateException) {
throw (IllegalStateException) ex;
}
throw new IllegalStateException("Could not load TestContextBootstrapper [" + clazz
+ "]. Specify @BootstrapWith's 'value' attribute "
+ "or make the default bootstrapper class available.", ex);
throw new IllegalStateException("Could not load TestContextBootstrapper [" + clazz +
"]. Specify @BootstrapWith's 'value' attribute or make the default bootstrapper class available.",
ex);
}
}
@ -160,14 +152,16 @@ abstract class BootstrapUtils { @@ -160,14 +152,16 @@ abstract class BootstrapUtils {
* @since 4.3
*/
private static Class<?> resolveExplicitTestContextBootstrapper(Class<?> testClass) {
MultiValueMap<String, Object> attributesMultiMap = AnnotatedElementUtils.getAllAnnotationAttributes(
testClass, BootstrapWith.class.getName());
List<Object> values = (attributesMultiMap == null ? null : attributesMultiMap.get(AnnotationUtils.VALUE));
MultiValueMap<String, Object> attributesMultiMap =
AnnotatedElementUtils.getAllAnnotationAttributes(testClass, BootstrapWith.class.getName());
List<Object> values = (attributesMultiMap != null ? attributesMultiMap.get(AnnotationUtils.VALUE) : null);
if (values == null) {
return null;
}
Assert.state(values.size() == 1, String.format("Configuration error: found multiple declarations of "
+ "@BootstrapWith on test class [%s] with values %s", testClass.getName(), values));
if (values.size() != 1) {
throw new IllegalStateException(String.format("Configuration error: found multiple declarations of " +
"@BootstrapWith on test class [%s] with values %s", testClass.getName(), values));
}
return (Class<?>) values.get(0);
}

10
spring-test/src/main/java/org/springframework/test/context/TestExecutionListener.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 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.
@ -20,14 +20,17 @@ package org.springframework.test.context; @@ -20,14 +20,17 @@ package org.springframework.test.context;
* {@code TestExecutionListener} defines a <em>listener</em> API for reacting to
* test execution events published by the {@link TestContextManager} with which
* the listener is registered.
*
* <p>Concrete implementations must provide a {@code public} no-args constructor,
* so that listeners can be instantiated transparently by tools and configuration
* mechanisms.
*
* <p>Implementations may optionally declare the position in which they should
* be ordered among the chain of default listeners via the
* {@link org.springframework.core.Ordered Ordered} interface or
* {@link org.springframework.core.annotation.Order @Order} annotation. See
* {@link TestContextBootstrapper#getTestExecutionListeners()} for details.
*
* <p>Spring provides the following out-of-the-box implementations (all of
* which implement {@code Ordered}):
* <ul>
@ -58,7 +61,6 @@ public interface TestExecutionListener { @@ -58,7 +61,6 @@ public interface TestExecutionListener {
* <em>before class</em> lifecycle callbacks.
* <p>If a given testing framework does not support <em>before class</em>
* lifecycle callbacks, this method will not be called for that framework.
*
* @param testContext the test context for the test; never {@code null}
* @throws Exception allows any exception to propagate
*/
@ -69,7 +71,6 @@ public interface TestExecutionListener { @@ -69,7 +71,6 @@ public interface TestExecutionListener {
* {@link TestContext test context}, for example by injecting dependencies.
* <p>This method should be called immediately after instantiation of the test
* instance but prior to any framework-specific lifecycle callbacks.
*
* @param testContext the test context for the test; never {@code null}
* @throws Exception allows any exception to propagate
*/
@ -82,7 +83,6 @@ public interface TestExecutionListener { @@ -82,7 +83,6 @@ public interface TestExecutionListener {
* fixtures.
* <p>This method should be called immediately prior to framework-specific
* <em>before</em> lifecycle callbacks.
*
* @param testContext the test context in which the test method will be
* executed; never {@code null}
* @throws Exception allows any exception to propagate
@ -96,7 +96,6 @@ public interface TestExecutionListener { @@ -96,7 +96,6 @@ public interface TestExecutionListener {
* fixtures.
* <p>This method should be called immediately after framework-specific
* <em>after</em> lifecycle callbacks.
*
* @param testContext the test context in which the test method was
* executed; never {@code null}
* @throws Exception allows any exception to propagate
@ -110,7 +109,6 @@ public interface TestExecutionListener { @@ -110,7 +109,6 @@ public interface TestExecutionListener {
* <em>after class</em> lifecycle callbacks.
* <p>If a given testing framework does not support <em>after class</em>
* lifecycle callbacks, this method will not be called for that framework.
*
* @param testContext the test context for the test; never {@code null}
* @throws Exception allows any exception to propagate
*/

86
spring-test/src/main/java/org/springframework/test/context/TestExecutionListeners.java

@ -1,5 +1,5 @@ @@ -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.
@ -30,8 +30,8 @@ import org.springframework.core.annotation.AliasFor; @@ -30,8 +30,8 @@ import org.springframework.core.annotation.AliasFor;
* which {@link TestExecutionListener TestExecutionListeners} should be
* registered with a {@link TestContextManager}.
*
* <p>Typically, {@code @TestExecutionListeners} will be used in conjunction with
* {@link ContextConfiguration @ContextConfiguration}.
* <p>Typically, {@code @TestExecutionListeners} will be used in conjunction
* with {@link ContextConfiguration @ContextConfiguration}.
*
* <p>As of Spring Framework 4.0, this annotation may be used as a
* <em>meta-annotation</em> to create custom <em>composed annotations</em>.
@ -48,42 +48,8 @@ import org.springframework.core.annotation.AliasFor; @@ -48,42 +48,8 @@ import org.springframework.core.annotation.AliasFor;
@Target(ElementType.TYPE)
public @interface TestExecutionListeners {
/**
* Enumeration of <em>modes</em> that dictate whether or not explicitly
* declared listeners are merged with the default listeners when
* {@code @TestExecutionListeners} is declared on a class that does
* <strong>not</strong> inherit listeners from a superclass.
* @since 4.1
*/
static enum MergeMode {
/**
* Indicates that locally declared listeners should replace the default
* listeners.
*/
REPLACE_DEFAULTS,
/**
* Indicates that locally declared listeners should be merged with the
* default listeners.
* <p>The merging algorithm ensures that duplicates are removed from
* the list and that the resulting set of merged listeners is sorted
* according to the semantics of
* {@link org.springframework.core.annotation.AnnotationAwareOrderComparator
* AnnotationAwareOrderComparator}. If a listener implements
* {@link org.springframework.core.Ordered Ordered} or is annotated
* with {@link org.springframework.core.annotation.Order @Order} it can
* influence the position in which it is merged with the defaults; otherwise,
* locally declared listeners will simply be appended to the list of default
* listeners when merged.
*/
MERGE_WITH_DEFAULTS
}
/**
* Alias for {@link #listeners}.
*
* <p>This attribute may <strong>not</strong> be used in conjunction with
* {@link #listeners}, but it may be used instead of {@link #listeners}.
*/
@ -93,10 +59,8 @@ public @interface TestExecutionListeners { @@ -93,10 +59,8 @@ public @interface TestExecutionListeners {
/**
* The {@link TestExecutionListener TestExecutionListeners} to register with
* the {@link TestContextManager}.
*
* <p>This attribute may <strong>not</strong> be used in conjunction with
* {@link #value}, but it may be used instead of {@link #value}.
*
* @see org.springframework.test.context.web.ServletTestExecutionListener
* @see org.springframework.test.context.support.DependencyInjectionTestExecutionListener
* @see org.springframework.test.context.support.DirtiesContextTestExecutionListener
@ -109,7 +73,6 @@ public @interface TestExecutionListeners { @@ -109,7 +73,6 @@ public @interface TestExecutionListeners {
/**
* Whether or not {@link #listeners TestExecutionListeners} from superclasses
* should be <em>inherited</em>.
*
* <p>The default value is {@code true}, which means that an annotated
* class will <em>inherit</em> the listeners defined by an annotated
* superclass. Specifically, the listeners for an annotated class will be
@ -122,21 +85,19 @@ public @interface TestExecutionListeners { @@ -122,21 +85,19 @@ public @interface TestExecutionListeners {
* {@code DependencyInjectionTestExecutionListener},
* {@code DirtiesContextTestExecutionListener}, <strong>and</strong>
* {@code TransactionalTestExecutionListener}, in that order.
*
* <pre class="code">
* &#064;TestExecutionListeners({
* DependencyInjectionTestExecutionListener.class,
* DirtiesContextTestExecutionListener.class
* DependencyInjectionTestExecutionListener.class,
* DirtiesContextTestExecutionListener.class
* })
* public abstract class AbstractBaseTest {
* // ...
* // ...
* }
*
* &#064;TestExecutionListeners(TransactionalTestExecutionListener.class)
* public class TransactionalTest extends AbstractBaseTest {
* // ...
* // ...
* }</pre>
*
* <p>If {@code inheritListeners} is set to {@code false}, the listeners for
* the annotated class will <em>shadow</em> and effectively replace any
* listeners defined by a superclass.
@ -158,4 +119,37 @@ public @interface TestExecutionListeners { @@ -158,4 +119,37 @@ public @interface TestExecutionListeners {
*/
MergeMode mergeMode() default MergeMode.REPLACE_DEFAULTS;
/**
* Enumeration of <em>modes</em> that dictate whether or not explicitly
* declared listeners are merged with the default listeners when
* {@code @TestExecutionListeners} is declared on a class that does
* <strong>not</strong> inherit listeners from a superclass.
* @since 4.1
*/
enum MergeMode {
/**
* Indicates that locally declared listeners should replace the default
* listeners.
*/
REPLACE_DEFAULTS,
/**
* Indicates that locally declared listeners should be merged with the
* default listeners.
* <p>The merging algorithm ensures that duplicates are removed from
* the list and that the resulting set of merged listeners is sorted
* according to the semantics of
* {@link org.springframework.core.annotation.AnnotationAwareOrderComparator
* AnnotationAwareOrderComparator}. If a listener implements
* {@link org.springframework.core.Ordered Ordered} or is annotated
* with {@link org.springframework.core.annotation.Order @Order} it can
* influence the position in which it is merged with the defaults; otherwise,
* locally declared listeners will simply be appended to the list of default
* listeners when merged.
*/
MERGE_WITH_DEFAULTS
}
}

30
spring-test/src/main/java/org/springframework/test/context/TestPropertySource.java

@ -1,5 +1,5 @@ @@ -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.
@ -90,10 +90,8 @@ public @interface TestPropertySource { @@ -90,10 +90,8 @@ public @interface TestPropertySource {
/**
* Alias for {@link #locations}.
*
* <p>This attribute may <strong>not</strong> be used in conjunction with
* {@link #locations}, but it may be used <em>instead</em> of {@link #locations}.
*
* @see #locations
*/
@AliasFor("locations")
@ -104,12 +102,10 @@ public @interface TestPropertySource { @@ -104,12 +102,10 @@ public @interface TestPropertySource {
* {@code Environment}'s set of {@code PropertySources}. Each location
* will be added to the enclosing {@code Environment} as its own property
* source, in the order declared.
*
* <h3>Supported File Formats</h3>
* <p>Both traditional and XML-based properties file formats are supported
* &mdash; for example, {@code "classpath:/com/example/test.properties"}
* or {@code "file:/path/to/file.xml"}.
*
* <h3>Path Resource Semantics</h3>
* <p>Each path will be interpreted as a Spring
* {@link org.springframework.core.io.Resource Resource}. A plain path
@ -128,17 +124,13 @@ public @interface TestPropertySource { @@ -128,17 +124,13 @@ public @interface TestPropertySource {
* in paths (i.e., <code>${...}</code>) will be
* {@linkplain org.springframework.core.env.Environment#resolveRequiredPlaceholders(String) resolved}
* against the {@code Environment}.
*
* <h3>Default Properties File Detection</h3>
* <p>See the class-level Javadoc for a discussion on detection of defaults.
*
* <h3>Precedence</h3>
* <p>Properties loaded from resource locations have lower precedence than
* inlined {@link #properties}.
*
* <p>This attribute may <strong>not</strong> be used in conjunction with
* {@link #value}, but it may be used <em>instead</em> of {@link #value}.
*
* @see #inheritLocations
* @see #value
* @see #properties
@ -150,18 +142,15 @@ public @interface TestPropertySource { @@ -150,18 +142,15 @@ public @interface TestPropertySource {
/**
* Whether or not test property source {@link #locations} from superclasses
* should be <em>inherited</em>.
*
* <p>The default value is {@code true}, which means that a test class will
* <em>inherit</em> property source locations defined by a superclass.
* Specifically, the property source locations for a test class will be
* appended to the list of property source locations defined by a superclass.
* Thus, subclasses have the option of <em>extending</em> the list of test
* property source locations.
*
* <p>If {@code inheritLocations} is set to {@code false}, the property
* source locations for the test class will <em>shadow</em> and effectively
* replace any property source locations defined by a superclass.
*
* <p>In the following example, the {@code ApplicationContext} for
* {@code BaseTest} will be loaded using only the {@code "base.properties"}
* file as a test property source. In contrast, the {@code ApplicationContext}
@ -172,13 +161,13 @@ public @interface TestPropertySource { @@ -172,13 +161,13 @@ public @interface TestPropertySource {
* &#064;TestPropertySource(&quot;base.properties&quot;)
* &#064;ContextConfiguration
* public class BaseTest {
* // ...
* // ...
* }
*
* &#064;TestPropertySource(&quot;extended.properties&quot;)
* &#064;ContextConfiguration
* public class ExtendedTest extends BaseTest {
* // ...
* // ...
* }
* </pre>
*
@ -193,7 +182,6 @@ public @interface TestPropertySource { @@ -193,7 +182,6 @@ public @interface TestPropertySource {
* {@code ApplicationContext} is loaded for the test. All key-value pairs
* will be added to the enclosing {@code Environment} as a single test
* {@code PropertySource} with the highest precedence.
*
* <h3>Supported Syntax</h3>
* <p>The supported syntax for key-value pairs is the same as the
* syntax defined for entries in a Java
@ -203,14 +191,11 @@ public @interface TestPropertySource { @@ -203,14 +191,11 @@ public @interface TestPropertySource {
* <li>{@code "key:value"}</li>
* <li>{@code "key value"}</li>
* </ul>
*
* <h3>Precedence</h3>
* <p>Properties declared via this attribute have higher precedence than
* properties loaded from resource {@link #locations}.
*
* <p>This attribute may be used in conjunction with {@link #value}
* <em>or</em> {@link #locations}.
*
* @see #inheritProperties
* @see #locations
* @see org.springframework.core.env.PropertySource
@ -220,17 +205,14 @@ public @interface TestPropertySource { @@ -220,17 +205,14 @@ public @interface TestPropertySource {
/**
* Whether or not inlined test {@link #properties} from superclasses should
* be <em>inherited</em>.
*
* <p>The default value is {@code true}, which means that a test class will
* <em>inherit</em> inlined properties defined by a superclass. Specifically,
* the inlined properties for a test class will be appended to the list of
* inlined properties defined by a superclass. Thus, subclasses have the
* option of <em>extending</em> the list of inlined test properties.
*
* <p>If {@code inheritProperties} is set to {@code false}, the inlined
* properties for the test class will <em>shadow</em> and effectively
* replace any inlined properties defined by a superclass.
*
* <p>In the following example, the {@code ApplicationContext} for
* {@code BaseTest} will be loaded using only the inlined {@code key1}
* property. In contrast, the {@code ApplicationContext} for
@ -240,16 +222,14 @@ public @interface TestPropertySource { @@ -240,16 +222,14 @@ public @interface TestPropertySource {
* &#064;TestPropertySource(properties = &quot;key1 = value1&quot;)
* &#064;ContextConfiguration
* public class BaseTest {
* // ...
* // ...
* }
*
* &#064;TestPropertySource(properties = &quot;key2 = value2&quot;)
* &#064;ContextConfiguration
* public class ExtendedTest extends BaseTest {
* // ...
* // ...
* }
* </pre>
*
* @see #properties
*/
boolean inheritProperties() default true;

76
spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java

@ -110,7 +110,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot @@ -110,7 +110,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
@Override
public TestContext buildTestContext() {
return new DefaultTestContext(getBootstrapContext().getTestClass(), buildMergedContextConfiguration(),
getCacheAwareContextLoaderDelegate());
getCacheAwareContextLoaderDelegate());
}
/**
@ -123,14 +123,14 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot @@ -123,14 +123,14 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
List<Class<? extends TestExecutionListener>> classesList = new ArrayList<Class<? extends TestExecutionListener>>();
boolean usingDefaults = false;
AnnotationDescriptor<TestExecutionListeners> descriptor = MetaAnnotationUtils.findAnnotationDescriptor(clazz,
annotationType);
AnnotationDescriptor<TestExecutionListeners> descriptor =
MetaAnnotationUtils.findAnnotationDescriptor(clazz, annotationType);
// Use defaults?
if (descriptor == null) {
if (logger.isDebugEnabled()) {
logger.debug(String.format("@TestExecutionListeners is not present for class [%s]: using defaults.",
clazz.getName()));
clazz.getName()));
}
usingDefaults = true;
classesList.addAll(getDefaultTestExecutionListenerClasses());
@ -142,21 +142,21 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot @@ -142,21 +142,21 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
TestExecutionListeners testExecutionListeners = descriptor.synthesizeAnnotation();
if (logger.isTraceEnabled()) {
logger.trace(String.format("Retrieved @TestExecutionListeners [%s] for declaring class [%s].",
testExecutionListeners, declaringClass.getName()));
testExecutionListeners, declaringClass.getName()));
}
boolean inheritListeners = testExecutionListeners.inheritListeners();
AnnotationDescriptor<TestExecutionListeners> superDescriptor = MetaAnnotationUtils.findAnnotationDescriptor(
descriptor.getRootDeclaringClass().getSuperclass(), annotationType);
AnnotationDescriptor<TestExecutionListeners> superDescriptor =
MetaAnnotationUtils.findAnnotationDescriptor(
descriptor.getRootDeclaringClass().getSuperclass(), annotationType);
// If there are no listeners to inherit, we might need to merge the
// locally declared listeners with the defaults.
if ((!inheritListeners || superDescriptor == null)
&& (testExecutionListeners.mergeMode() == MergeMode.MERGE_WITH_DEFAULTS)) {
if ((!inheritListeners || superDescriptor == null) &&
testExecutionListeners.mergeMode() == MergeMode.MERGE_WITH_DEFAULTS) {
if (logger.isDebugEnabled()) {
logger.debug(String.format(
"Merging default listeners with listeners configured via @TestExecutionListeners for class [%s].",
descriptor.getRootDeclaringClass().getName()));
logger.debug(String.format("Merging default listeners with listeners configured via " +
"@TestExecutionListeners for class [%s].", descriptor.getRootDeclaringClass().getName()));
}
usingDefaults = true;
classesList.addAll(getDefaultTestExecutionListenerClasses());
@ -206,10 +206,10 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot @@ -206,10 +206,10 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
}
if (ncdfe != null) {
if (logger.isInfoEnabled()) {
logger.info(String.format("Could not instantiate TestExecutionListener [%s]. "
+ "Specify custom listener classes or make the default listener classes "
+ "(and their required dependencies) available. Offending class: [%s]",
listenerClass.getName(), ncdfe.getMessage()));
logger.info(String.format("Could not instantiate TestExecutionListener [%s]. " +
"Specify custom listener classes or make the default listener classes " +
"(and their required dependencies) available. Offending class: [%s]",
listenerClass.getName(), ncdfe.getMessage()));
}
}
}
@ -234,8 +234,8 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot @@ -234,8 +234,8 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not load default TestExecutionListener class [" + className
+ "]. Specify custom listener classes or make the default listener classes available.", ex);
logger.debug("Could not load default TestExecutionListener class [" + className +
"]. Specify custom listener classes or make the default listener classes available.", ex);
}
}
}
@ -254,12 +254,11 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot @@ -254,12 +254,11 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
* @see SpringFactoriesLoader#loadFactoryNames
*/
protected List<String> getDefaultTestExecutionListenerClassNames() {
final List<String> classNames = SpringFactoriesLoader.loadFactoryNames(TestExecutionListener.class,
getClass().getClassLoader());
List<String> classNames =
SpringFactoriesLoader.loadFactoryNames(TestExecutionListener.class, getClass().getClassLoader());
if (logger.isInfoEnabled()) {
logger.info(String.format("Loaded default TestExecutionListener class names from location [%s]: %s",
SpringFactoriesLoader.FACTORIES_RESOURCE_LOCATION, classNames));
SpringFactoriesLoader.FACTORIES_RESOURCE_LOCATION, classNames));
}
return Collections.unmodifiableList(classNames);
}
@ -273,13 +272,14 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot @@ -273,13 +272,14 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
Class<?> testClass = getBootstrapContext().getTestClass();
CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate = getCacheAwareContextLoaderDelegate();
if (MetaAnnotationUtils.findAnnotationDescriptorForTypes(testClass, ContextConfiguration.class,
ContextHierarchy.class) == null) {
if (MetaAnnotationUtils.findAnnotationDescriptorForTypes(
testClass, ContextConfiguration.class, ContextHierarchy.class) == null) {
return buildDefaultMergedContextConfiguration(testClass, cacheAwareContextLoaderDelegate);
}
if (AnnotationUtils.findAnnotation(testClass, ContextHierarchy.class) != null) {
Map<String, List<ContextConfigurationAttributes>> hierarchyMap = ContextLoaderUtils.buildContextHierarchyMap(testClass);
Map<String, List<ContextConfigurationAttributes>> hierarchyMap =
ContextLoaderUtils.buildContextHierarchyMap(testClass);
MergedContextConfiguration parentConfig = null;
MergedContextConfiguration mergedConfig = null;
@ -293,8 +293,8 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot @@ -293,8 +293,8 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
Assert.notEmpty(reversedList, "ContextConfigurationAttributes list must not be empty");
Class<?> declaringClass = reversedList.get(0).getDeclaringClass();
mergedConfig = buildMergedContextConfiguration(declaringClass, reversedList, parentConfig,
cacheAwareContextLoaderDelegate, true);
mergedConfig = buildMergedContextConfiguration(
declaringClass, reversedList, parentConfig, cacheAwareContextLoaderDelegate, true);
parentConfig = mergedConfig;
}
@ -303,8 +303,8 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot @@ -303,8 +303,8 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
}
else {
return buildMergedContextConfiguration(testClass,
ContextLoaderUtils.resolveContextConfigurationAttributes(testClass), null,
cacheAwareContextLoaderDelegate, true);
ContextLoaderUtils.resolveContextConfigurationAttributes(testClass),
null, cacheAwareContextLoaderDelegate, true);
}
}
@ -390,14 +390,16 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot @@ -390,14 +390,16 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
Set<ContextCustomizer> contextCustomizers = getContextCustomizers(testClass,
Collections.unmodifiableList(configAttributesList));
if (requireLocationsClassesOrInitializers && areAllEmpty(locations, classes, initializers, contextCustomizers)) {
if (requireLocationsClassesOrInitializers &&
areAllEmpty(locations, classes, initializers, contextCustomizers)) {
throw new IllegalStateException(String.format(
"%s was unable to detect defaults, and no ApplicationContextInitializers "
+ "or ContextCustomizers were declared for context configuration attributes %s",
"%s was unable to detect defaults, and no ApplicationContextInitializers " +
"or ContextCustomizers were declared for context configuration attributes %s",
contextLoader.getClass().getSimpleName(), configAttributesList));
}
MergedTestPropertySources mergedTestPropertySources = TestPropertySourceUtils.buildMergedTestPropertySources(testClass);
MergedTestPropertySources mergedTestPropertySources =
TestPropertySourceUtils.buildMergedTestPropertySources(testClass);
MergedContextConfiguration mergedConfig = new MergedContextConfiguration(testClass,
StringUtils.toStringArray(locations),
ClassUtils.toClassArray(classes),
@ -485,7 +487,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot @@ -485,7 +487,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
}
if (logger.isTraceEnabled()) {
logger.trace(String.format("Using ContextLoader class [%s] for test class [%s]",
contextLoaderClass.getName(), testClass.getName()));
contextLoaderClass.getName(), testClass.getName()));
}
return BeanUtils.instantiateClass(contextLoaderClass, ContextLoader.class);
}
@ -518,14 +520,14 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot @@ -518,14 +520,14 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
for (ContextConfigurationAttributes configAttributes : configAttributesList) {
if (logger.isTraceEnabled()) {
logger.trace(String.format("Resolving ContextLoader for context configuration attributes %s",
configAttributes));
configAttributes));
}
Class<? extends ContextLoader> contextLoaderClass = configAttributes.getContextLoaderClass();
if (ContextLoader.class != contextLoaderClass) {
if (logger.isDebugEnabled()) {
logger.debug(String.format(
"Found explicit ContextLoader class [%s] for context configuration attributes %s",
contextLoaderClass.getName(), configAttributes));
"Found explicit ContextLoader class [%s] for context configuration attributes %s",
contextLoaderClass.getName(), configAttributes));
}
return contextLoaderClass;
}

21
spring-test/src/main/java/org/springframework/test/util/MetaAnnotationUtils.java

@ -98,11 +98,10 @@ public abstract class MetaAnnotationUtils { @@ -98,11 +98,10 @@ public abstract class MetaAnnotationUtils {
* @return the corresponding annotation descriptor if the annotation was found;
* otherwise {@code null}
*/
private static <T extends Annotation> AnnotationDescriptor<T> findAnnotationDescriptor(Class<?> clazz,
Set<Annotation> visited, Class<T> annotationType) {
private static <T extends Annotation> AnnotationDescriptor<T> findAnnotationDescriptor(
Class<?> clazz, Set<Annotation> visited, Class<T> annotationType) {
Assert.notNull(annotationType, "Annotation type must not be null");
if (clazz == null || Object.class == clazz) {
return null;
}
@ -115,11 +114,11 @@ public abstract class MetaAnnotationUtils { @@ -115,11 +114,11 @@ public abstract class MetaAnnotationUtils {
// Declared on a composed annotation (i.e., as a meta-annotation)?
for (Annotation composedAnnotation : clazz.getDeclaredAnnotations()) {
if (!AnnotationUtils.isInJavaLangAnnotationPackage(composedAnnotation) && visited.add(composedAnnotation)) {
AnnotationDescriptor<T> descriptor = findAnnotationDescriptor(composedAnnotation.annotationType(),
visited, annotationType);
AnnotationDescriptor<T> descriptor = findAnnotationDescriptor(
composedAnnotation.annotationType(), visited, annotationType);
if (descriptor != null) {
return new AnnotationDescriptor<T>(clazz, descriptor.getDeclaringClass(), composedAnnotation,
descriptor.getAnnotation());
return new AnnotationDescriptor<T>(
clazz, descriptor.getDeclaringClass(), composedAnnotation, descriptor.getAnnotation());
}
}
}
@ -287,8 +286,8 @@ public abstract class MetaAnnotationUtils { @@ -287,8 +286,8 @@ public abstract class MetaAnnotationUtils {
this.declaringClass = declaringClass;
this.composedAnnotation = composedAnnotation;
this.annotation = annotation;
this.annotationAttributes = AnnotatedElementUtils.findMergedAnnotationAttributes(rootDeclaringClass,
annotation.annotationType().getName(), false, false);
this.annotationAttributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
rootDeclaringClass, annotation.annotationType().getName(), false, false);
}
public Class<?> getRootDeclaringClass() {
@ -314,8 +313,8 @@ public abstract class MetaAnnotationUtils { @@ -314,8 +313,8 @@ public abstract class MetaAnnotationUtils {
*/
@SuppressWarnings("unchecked")
public T synthesizeAnnotation() {
return AnnotationUtils.synthesizeAnnotation(getAnnotationAttributes(), (Class<T>) getAnnotationType(),
getRootDeclaringClass());
return AnnotationUtils.synthesizeAnnotation(
getAnnotationAttributes(), (Class<T>) getAnnotationType(), getRootDeclaringClass());
}
public Class<? extends Annotation> getAnnotationType() {

90
spring-test/src/test/java/org/springframework/test/context/TestExecutionListenersTests.java

@ -1,5 +1,5 @@ @@ -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.
@ -55,9 +55,9 @@ public class TestExecutionListenersTests { @@ -55,9 +55,9 @@ public class TestExecutionListenersTests {
@Test
public void defaultListeners() {
List<Class<?>> expected = asList(ServletTestExecutionListener.class,
DirtiesContextBeforeModesTestExecutionListener.class, DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class,
SqlScriptsTestExecutionListener.class);
DirtiesContextBeforeModesTestExecutionListener.class, DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class,
SqlScriptsTestExecutionListener.class);
assertRegisteredListeners(DefaultListenersTestCase.class, expected);
}
@ -67,9 +67,9 @@ public class TestExecutionListenersTests { @@ -67,9 +67,9 @@ public class TestExecutionListenersTests {
@Test
public void defaultListenersMergedWithCustomListenerPrepended() {
List<Class<?>> expected = asList(QuuxTestExecutionListener.class, ServletTestExecutionListener.class,
DirtiesContextBeforeModesTestExecutionListener.class, DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class,
SqlScriptsTestExecutionListener.class);
DirtiesContextBeforeModesTestExecutionListener.class, DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class,
SqlScriptsTestExecutionListener.class);
assertRegisteredListeners(MergedDefaultListenersWithCustomListenerPrependedTestCase.class, expected);
}
@ -79,9 +79,9 @@ public class TestExecutionListenersTests { @@ -79,9 +79,9 @@ public class TestExecutionListenersTests {
@Test
public void defaultListenersMergedWithCustomListenerAppended() {
List<Class<?>> expected = asList(ServletTestExecutionListener.class,
DirtiesContextBeforeModesTestExecutionListener.class, DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class,
SqlScriptsTestExecutionListener.class, BazTestExecutionListener.class);
DirtiesContextBeforeModesTestExecutionListener.class, DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class,
SqlScriptsTestExecutionListener.class, BazTestExecutionListener.class);
assertRegisteredListeners(MergedDefaultListenersWithCustomListenerAppendedTestCase.class, expected);
}
@ -91,9 +91,9 @@ public class TestExecutionListenersTests { @@ -91,9 +91,9 @@ public class TestExecutionListenersTests {
@Test
public void defaultListenersMergedWithCustomListenerInserted() {
List<Class<?>> expected = asList(ServletTestExecutionListener.class,
DirtiesContextBeforeModesTestExecutionListener.class, DependencyInjectionTestExecutionListener.class,
BarTestExecutionListener.class, DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class, SqlScriptsTestExecutionListener.class);
DirtiesContextBeforeModesTestExecutionListener.class, DependencyInjectionTestExecutionListener.class,
BarTestExecutionListener.class, DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class, SqlScriptsTestExecutionListener.class);
assertRegisteredListeners(MergedDefaultListenersWithCustomListenerInsertedTestCase.class, expected);
}
@ -107,7 +107,7 @@ public class TestExecutionListenersTests { @@ -107,7 +107,7 @@ public class TestExecutionListenersTests {
assertRegisteredListeners(InheritedDefaultListenersTestCase.class, asList(QuuxTestExecutionListener.class));
assertRegisteredListeners(SubInheritedDefaultListenersTestCase.class, asList(QuuxTestExecutionListener.class));
assertRegisteredListeners(SubSubInheritedDefaultListenersTestCase.class,
asList(QuuxTestExecutionListener.class, EnigmaTestExecutionListener.class));
asList(QuuxTestExecutionListener.class, EnigmaTestExecutionListener.class));
}
@Test
@ -172,13 +172,13 @@ public class TestExecutionListenersTests { @@ -172,13 +172,13 @@ public class TestExecutionListenersTests {
private void assertRegisteredListeners(Class<?> testClass, List<Class<?>> expected) {
TestContextManager testContextManager = new TestContextManager(testClass);
assertEquals("TELs registered for " + testClass.getSimpleName(), names(expected),
names(classes(testContextManager)));
names(classes(testContextManager)));
}
private void assertNumRegisteredListeners(Class<?> testClass, int expected) {
TestContextManager testContextManager = new TestContextManager(testClass);
assertEquals("Num registered TELs for " + testClass, expected,
testContextManager.getTestExecutionListeners().size());
testContextManager.getTestExecutionListeners().size());
}
@ -187,8 +187,9 @@ public class TestExecutionListenersTests { @@ -187,8 +187,9 @@ public class TestExecutionListenersTests {
static class DefaultListenersTestCase {
}
@TestExecutionListeners(listeners = { QuuxTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class }, mergeMode = MERGE_WITH_DEFAULTS)
@TestExecutionListeners(
listeners = {QuuxTestExecutionListener.class, DependencyInjectionTestExecutionListener.class},
mergeMode = MERGE_WITH_DEFAULTS)
static class MergedDefaultListenersWithCustomListenerPrependedTestCase {
}
@ -211,12 +212,12 @@ public class TestExecutionListenersTests { @@ -211,12 +212,12 @@ public class TestExecutionListenersTests {
static class SubSubInheritedDefaultListenersTestCase extends SubInheritedDefaultListenersTestCase {
}
@TestExecutionListeners(listeners = { QuuxTestExecutionListener.class }, inheritListeners = false)
@TestExecutionListeners(listeners = QuuxTestExecutionListener.class, inheritListeners = false)
static class NonInheritedDefaultListenersTestCase extends InheritedDefaultListenersTestCase {
}
@TestExecutionListeners({ FooTestExecutionListener.class, BarTestExecutionListener.class,
BazTestExecutionListener.class })
@TestExecutionListeners(
{FooTestExecutionListener.class, BarTestExecutionListener.class, BazTestExecutionListener.class})
static class ExplicitListenersTestCase {
}
@ -232,36 +233,36 @@ public class TestExecutionListenersTests { @@ -232,36 +233,36 @@ public class TestExecutionListenersTests {
static class DuplicateListenersConfigTestCase {
}
@TestExecutionListeners({//
FooTestExecutionListener.class,//
BarTestExecutionListener.class,//
BazTestExecutionListener.class //
@TestExecutionListeners({
FooTestExecutionListener.class,
BarTestExecutionListener.class,
BazTestExecutionListener.class
})
@Retention(RetentionPolicy.RUNTIME)
static @interface MetaListeners {
@interface MetaListeners {
}
@TestExecutionListeners(QuuxTestExecutionListener.class)
@Retention(RetentionPolicy.RUNTIME)
static @interface MetaInheritedListeners {
@interface MetaInheritedListeners {
}
@TestExecutionListeners(listeners = QuuxTestExecutionListener.class, inheritListeners = false)
@Retention(RetentionPolicy.RUNTIME)
static @interface MetaNonInheritedListeners {
@interface MetaNonInheritedListeners {
}
@TestExecutionListeners
@Retention(RetentionPolicy.RUNTIME)
static @interface MetaListenersWithOverrides {
@interface MetaListenersWithOverrides {
Class<? extends TestExecutionListener>[] listeners() default { FooTestExecutionListener.class,
BarTestExecutionListener.class };
Class<? extends TestExecutionListener>[] listeners() default
{FooTestExecutionListener.class, BarTestExecutionListener.class};
}
@TestExecutionListeners
@Retention(RetentionPolicy.RUNTIME)
static @interface MetaInheritedListenersWithOverrides {
@interface MetaInheritedListenersWithOverrides {
Class<? extends TestExecutionListener>[] listeners() default QuuxTestExecutionListener.class;
@ -270,7 +271,7 @@ public class TestExecutionListenersTests { @@ -270,7 +271,7 @@ public class TestExecutionListenersTests {
@TestExecutionListeners
@Retention(RetentionPolicy.RUNTIME)
static @interface MetaNonInheritedListenersWithOverrides {
@interface MetaNonInheritedListenersWithOverrides {
Class<? extends TestExecutionListener>[] listeners() default QuuxTestExecutionListener.class;
@ -289,24 +290,23 @@ public class TestExecutionListenersTests { @@ -289,24 +290,23 @@ public class TestExecutionListenersTests {
static class MetaNonInheritedListenersTestCase extends MetaInheritedListenersTestCase {
}
@MetaListenersWithOverrides(listeners = {//
FooTestExecutionListener.class,//
BarTestExecutionListener.class,//
BazTestExecutionListener.class //
@MetaListenersWithOverrides(listeners = {
FooTestExecutionListener.class,
BarTestExecutionListener.class,
BazTestExecutionListener.class
})
static class MetaWithOverridesTestCase {
}
@MetaInheritedListenersWithOverrides(listeners = { FooTestExecutionListener.class, BarTestExecutionListener.class })
@MetaInheritedListenersWithOverrides(listeners = {FooTestExecutionListener.class, BarTestExecutionListener.class})
static class MetaInheritedListenersWithOverridesTestCase extends MetaWithOverridesTestCase {
}
@MetaNonInheritedListenersWithOverrides(listeners = {//
FooTestExecutionListener.class,//
BarTestExecutionListener.class,//
BazTestExecutionListener.class //
},//
inheritListeners = true)
@MetaNonInheritedListenersWithOverrides(listeners = {
FooTestExecutionListener.class,
BarTestExecutionListener.class,
BazTestExecutionListener.class
}, inheritListeners = true)
static class MetaNonInheritedListenersWithOverridesTestCase extends MetaInheritedListenersWithOverridesTestCase {
}
@ -342,4 +342,4 @@ public class TestExecutionListenersTests { @@ -342,4 +342,4 @@ public class TestExecutionListenersTests {
static class EnigmaTestExecutionListener extends AbstractTestExecutionListener {
}
}
}

Loading…
Cancel
Save