@ -31,7 +31,9 @@ import org.springframework.beans.BeanUtils;
import org.springframework.context.ApplicationContextInitializer ;
import org.springframework.context.ApplicationContextInitializer ;
import org.springframework.context.ConfigurableApplicationContext ;
import org.springframework.context.ConfigurableApplicationContext ;
import org.springframework.core.annotation.AnnotationAttributes ;
import org.springframework.core.annotation.AnnotationAttributes ;
import org.springframework.core.annotation.AnnotationAwareOrderComparator ;
import org.springframework.core.annotation.AnnotationUtils ;
import org.springframework.core.annotation.AnnotationUtils ;
import org.springframework.core.io.support.SpringFactoriesLoader ;
import org.springframework.test.context.BootstrapContext ;
import org.springframework.test.context.BootstrapContext ;
import org.springframework.test.context.CacheAwareContextLoaderDelegate ;
import org.springframework.test.context.CacheAwareContextLoaderDelegate ;
import org.springframework.test.context.ContextConfiguration ;
import org.springframework.test.context.ContextConfiguration ;
@ -55,11 +57,10 @@ import org.springframework.util.StringUtils;
* provides most of the behavior required by a bootstrapper .
* provides most of the behavior required by a bootstrapper .
*
*
* < p > Concrete subclasses typically will only need to provide implementations for
* < p > Concrete subclasses typically will only need to provide implementations for
* the following { @code abstract } methods :
* the following methods :
* < ul >
* < ul >
* < li > { @link # getDefaultTestExecutionListenerClassNames }
* < li > { @link # getDefaultContextLoaderClass }
* < li > { @link # getDefaultContextLoaderClass }
* < li > { @link # build MergedContextConfiguration}
* < li > { @link # process MergedContextConfiguration}
* < / ul >
* < / ul >
*
*
* @author Sam Brannen
* @author Sam Brannen
@ -98,6 +99,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
Class < ? > clazz = getBootstrapContext ( ) . getTestClass ( ) ;
Class < ? > clazz = getBootstrapContext ( ) . getTestClass ( ) ;
Class < TestExecutionListeners > annotationType = TestExecutionListeners . class ;
Class < TestExecutionListeners > annotationType = TestExecutionListeners . class ;
List < Class < ? extends TestExecutionListener > > classesList = new ArrayList < Class < ? extends TestExecutionListener > > ( ) ;
List < Class < ? extends TestExecutionListener > > classesList = new ArrayList < Class < ? extends TestExecutionListener > > ( ) ;
boolean usingDefaults = false ;
AnnotationDescriptor < TestExecutionListeners > descriptor = MetaAnnotationUtils . findAnnotationDescriptor ( clazz ,
AnnotationDescriptor < TestExecutionListeners > descriptor = MetaAnnotationUtils . findAnnotationDescriptor ( clazz ,
annotationType ) ;
annotationType ) ;
@ -105,9 +107,10 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
// Use defaults?
// Use defaults?
if ( descriptor = = null ) {
if ( descriptor = = null ) {
if ( logger . isDebugEnabled ( ) ) {
if ( logger . isDebugEnabled ( ) ) {
logger . debug ( "@TestExecutionListeners is not present for class [" + clazz . getName ( )
logger . debug ( String . format ( "@TestExecutionListeners is not present for class [%s]: using defaults." ,
+ "]: using defaults." ) ;
clazz . getName ( ) ) ) ;
}
}
usingDefaults = true ;
classesList . addAll ( getDefaultTestExecutionListenerClasses ( ) ) ;
classesList . addAll ( getDefaultTestExecutionListenerClasses ( ) ) ;
}
}
else {
else {
@ -142,6 +145,20 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
}
}
}
}
List < TestExecutionListener > listeners = instantiateListeners ( classesList ) ;
// Sort by Ordered/@Order if we loaded default listeners.
if ( usingDefaults ) {
AnnotationAwareOrderComparator . sort ( listeners ) ;
}
if ( logger . isInfoEnabled ( ) ) {
logger . info ( String . format ( "Using TestExecutionListeners: %s" , listeners ) ) ;
}
return listeners ;
}
private List < TestExecutionListener > instantiateListeners ( List < Class < ? extends TestExecutionListener > > classesList ) {
List < TestExecutionListener > listeners = new ArrayList < TestExecutionListener > ( classesList . size ( ) ) ;
List < TestExecutionListener > listeners = new ArrayList < TestExecutionListener > ( classesList . size ( ) ) ;
for ( Class < ? extends TestExecutionListener > listenerClass : classesList ) {
for ( Class < ? extends TestExecutionListener > listenerClass : classesList ) {
NoClassDefFoundError ncdfe = null ;
NoClassDefFoundError ncdfe = null ;
@ -194,6 +211,28 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
return defaultListenerClasses ;
return defaultListenerClasses ;
}
}
/ * *
* Get the names of the default { @link TestExecutionListener } classes for
* this bootstrapper .
* < p > The default implementation looks up all
* { @code org . springframework . test . context . TestExecutionListener } entries
* configured in all { @code META - INF / spring . factories } files on the classpath .
* < p > This method is invoked by { @link # getDefaultTestExecutionListenerClasses ( ) } .
* @return an < em > unmodifiable < / em > list of names of default { @code TestExecutionListener }
* classes
* @see SpringFactoriesLoader # loadFactoryNames
* /
protected List < String > getDefaultTestExecutionListenerClassNames ( ) {
final 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 ) ) ;
}
return Collections . unmodifiableList ( classNames ) ;
}
/ * *
/ * *
* { @inheritDoc }
* { @inheritDoc }
* /
* /
@ -302,9 +341,11 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
String [ ] activeProfiles = ActiveProfilesUtils . resolveActiveProfiles ( testClass ) ;
String [ ] activeProfiles = ActiveProfilesUtils . resolveActiveProfiles ( testClass ) ;
MergedTestPropertySources mergedTestPropertySources = TestPropertySourceUtils . buildMergedTestPropertySources ( testClass ) ;
MergedTestPropertySources mergedTestPropertySources = TestPropertySourceUtils . buildMergedTestPropertySources ( testClass ) ;
return buildMergedContextConfiguration ( testClass , locations , classes , initializerClasses , activeProfiles ,
MergedContextConfiguration mergedConfig = new MergedContextConfiguration ( testClass , locations , classes ,
mergedTestPropertySources . getLocations ( ) , mergedTestPropertySources . getProperties ( ) , contextLoader ,
initializerClasses , activeProfiles , mergedTestPropertySources . getLocations ( ) ,
cacheAwareContextLoaderDelegate , parentConfig ) ;
mergedTestPropertySources . getProperties ( ) , contextLoader , cacheAwareContextLoaderDelegate , parentConfig ) ;
return processMergedContextConfiguration ( mergedConfig ) ;
}
}
/ * *
/ * *
@ -383,15 +424,6 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
return null ;
return null ;
}
}
/ * *
* Get the names of the default { @link TestExecutionListener } classes for
* this bootstrapper .
* < p > This method is invoked by { @link # getDefaultTestExecutionListenerClasses ( ) } .
* @return an < em > unmodifiable < / em > list of names of default { @code
* TestExecutionListener } classes
* /
protected abstract List < String > getDefaultTestExecutionListenerClassNames ( ) ;
/ * *
/ * *
* Determine the default { @link ContextLoader } class to use for the supplied
* Determine the default { @link ContextLoader } class to use for the supplied
* test class .
* test class .
@ -403,34 +435,19 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
protected abstract Class < ? extends ContextLoader > getDefaultContextLoaderClass ( Class < ? > testClass ) ;
protected abstract Class < ? extends ContextLoader > getDefaultContextLoaderClass ( Class < ? > testClass ) ;
/ * *
/ * *
* Build a { @link MergedContextConfiguration } instance from the supplied ,
* Process the supplied , newly instantiated { @link MergedContextConfiguration } instance .
* merged values .
* < p > The returned { @link MergedContextConfiguration } instance may be a wrapper
* < p > Concrete subclasses typically will only need to instantiate
* around or a replacement for the original .
* { @link MergedContextConfiguration } ( or a specialized subclass thereof )
* < p > The default implementation simply returns the supplied instance unmodified .
* from the provided values ; further processing and merging of values is likely
* < p > Concrete subclasses may choose to return a specialized subclass of
* unnecessary .
* { @link MergedContextConfiguration } based on properties in the supplied instance .
* @param testClass the test class for which the { @code MergedContextConfiguration }
* @param mergedConfig the { @code MergedContextConfiguration } to process ;
* should be built ( must not be { @code null } )
* never { @code null }
* @param locations the merged resource locations
* @return a fully initialized { @code MergedContextConfiguration } ; never
* @param classes the merged annotated classes
* { @code null }
* @param initializerClasses 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 the cache - aware context loader delegate
* to be provided to the instantiated { @code MergedContextConfiguration }
* @param parentConfig the merged context configuration for the parent application
* context in a context hierarchy , or { @code null } if there is no parent
* @return the fully initialized { @code MergedContextConfiguration }
* /
* /
protected abstract MergedContextConfiguration buildMergedContextConfiguration (
protected MergedContextConfiguration processMergedContextConfiguration ( MergedContextConfiguration mergedConfig ) {
Class < ? > testClass ,
return mergedConfig ;
String [ ] locations ,
}
Class < ? > [ ] classes ,
Set < Class < ? extends ApplicationContextInitializer < ? extends ConfigurableApplicationContext > > > initializerClasses ,
String [ ] activeProfiles , String [ ] propertySourceLocations , String [ ] propertySourceProperties ,
ContextLoader contextLoader , CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate ,
MergedContextConfiguration parentConfig ) ;
}
}