Skip to content

@TestExecutionListeners is not fully supported as a meta-annotation [SPR-12661] #17261

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
spring-projects-issues opened this issue Jan 23, 2015 · 3 comments
Assignees
Labels
in: test Issues in the test module status: duplicate A duplicate of another issue

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Jan 23, 2015

John Bass opened SPR-12661 and commented

Status Quo

When using TestNG and deriving a test from AbstractTestNGSpringContextTests, @TestExecutionListeners as a meta-annotation doesn't seem to be working.

Here's a little 3-class test:

@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Documented
@TestExecutionListeners(listeners=MyTestExecutionListener.class, mergeMode=MergeMode.MERGE_WITH_DEFAULTS)
public @interface Composed {
}
public class MyTestExecutionListener extends AbstractTestExecutionListener {
	private static final Logger logger = LoggerFactory.getLogger( MyTestExecutionListener.class );
	
	@Override
	public void prepareTestInstance( final TestContext testContext ) throws Exception {
		logger.trace( "prepareTestInstance()" );
	}

}
@ContextConfiguration
@Composed
public class Experiment extends AbstractTestNGSpringContextTests {
	@Configuration
	static class Configurer {
		@Bean
		public Object object() {
			return new Object();
		}
	}
	
	@Test
	public void aTest() {
	}
}

Here's some output when this is run:

[org.springframework.test.context.BootstrapUtils] - Instantiating TestContextBootstrapper from class [org.springframework.test.context.support.DefaultTestContextBootstrapper]
[org.springframework.test.context.support.ContextLoaderUtils] - Retrieved @ContextConfiguration attributes [{name=, value=[], classes=[], loader=interface org.springframework.test.context.ContextLoader, locations=[], initializers=[], inheritLocations=true, inheritInitializers=true}] for declaring class [com.comcast.cpt.test.Experiment].
[org.springframework.test.context.support.ContextLoaderUtils] - Resolved context configuration attributes: [ContextConfigurationAttributes@5a49cb8c declaringClass = 'com.comcast.cpt.test.Experiment', classes = '{}', locations = '{}', inheritLocations = true, initializers = '{}', inheritInitializers = true, name = [null], contextLoaderClass = 'org.springframework.test.context.ContextLoader']
[org.springframework.test.context.support.DefaultTestContextBootstrapper] - Resolving ContextLoader for context configuration attributes [ContextConfigurationAttributes@5a49cb8c declaringClass = 'com.comcast.cpt.test.Experiment', classes = '{}', locations = '{}', inheritLocations = true, initializers = '{}', inheritInitializers = true, name = [null], contextLoaderClass = 'org.springframework.test.context.ContextLoader']
[org.springframework.test.context.support.DefaultTestContextBootstrapper] - Using ContextLoader class [org.springframework.test.context.support.DelegatingSmartContextLoader] for test class [com.comcast.cpt.test.Experiment]
[org.springframework.test.context.support.DefaultTestContextBootstrapper] - Processing locations and classes for context configuration attributes [ContextConfigurationAttributes@5a49cb8c declaringClass = 'com.comcast.cpt.test.Experiment', classes = '{}', locations = '{}', inheritLocations = true, initializers = '{}', inheritInitializers = true, name = [null], contextLoaderClass = 'org.springframework.test.context.ContextLoader']
[org.springframework.test.context.support.AbstractDelegatingSmartContextLoader] - Delegating to GenericXmlContextLoader to process context configuration [ContextConfigurationAttributes@5a49cb8c declaringClass = 'com.comcast.cpt.test.Experiment', classes = '{}', locations = '{}', inheritLocations = true, initializers = '{}', inheritInitializers = true, name = [null], contextLoaderClass = 'org.springframework.test.context.ContextLoader'].
[org.springframework.test.context.support.AbstractContextLoader] - Did not detect default resource location for test class [com.comcast.cpt.test.Experiment]: class path resource [com/comcast/cpt/test/Experiment-context.xml] does not exist
[org.springframework.test.context.support.AbstractContextLoader] - Could not detect default resource locations for test class [com.comcast.cpt.test.Experiment]: no resource found for suffixes {-context.xml}.
[org.springframework.test.context.support.AbstractDelegatingSmartContextLoader] - Delegating to AnnotationConfigContextLoader to process context configuration [ContextConfigurationAttributes@5a49cb8c declaringClass = 'com.comcast.cpt.test.Experiment', classes = '{}', locations = '{}', inheritLocations = true, initializers = '{}', inheritInitializers = true, name = [null], contextLoaderClass = 'org.springframework.test.context.ContextLoader'].
[org.springframework.test.context.support.AbstractDelegatingSmartContextLoader] - AnnotationConfigContextLoader detected default configuration classes for context configuration [ContextConfigurationAttributes@5a49cb8c declaringClass = 'com.comcast.cpt.test.Experiment', classes = '{class com.comcast.cpt.test.Experiment$Configurer}', locations = '{}', inheritLocations = true, initializers = '{}', inheritInitializers = true, name = [null], contextLoaderClass = 'org.springframework.test.context.ContextLoader'].
[org.springframework.test.context.support.ApplicationContextInitializerUtils] - Processing context initializers for context configuration attributes [ContextConfigurationAttributes@5a49cb8c declaringClass = 'com.comcast.cpt.test.Experiment', classes = '{class com.comcast.cpt.test.Experiment$Configurer}', locations = '{}', inheritLocations = true, initializers = '{}', inheritInitializers = true, name = [null], contextLoaderClass = 'org.springframework.test.context.ContextLoader']
[org.springframework.test.context.support.ActiveProfilesUtils] - Could not find an 'annotation declaring class' for annotation type [org.springframework.test.context.ActiveProfiles] and class [com.comcast.cpt.test.Experiment]

// notice this interesting line ...
[org.springframework.test.context.support.DefaultTestContextBootstrapper] - Retrieved @TestExecutionListeners attributes [{value=[class org.springframework.test.context.web.ServletTestExecutionListener,class org.springframework.test.context.support.DependencyInjectionTestExecutionListener,class org.springframework.test.context.support.DirtiesContextTestExecutionListener], listeners=[], inheritListeners=true, mergeMode=REPLACE_DEFAULTS}] for declaring class [com.comcast.cpt.test.Composed].

[org.springframework.test.context.support.DefaultTestContextBootstrapper] - Retrieved @TestExecutionListeners attributes [{value=[class org.springframework.test.context.web.ServletTestExecutionListener,class org.springframework.test.context.support.DependencyInjectionTestExecutionListener,class org.springframework.test.context.support.DirtiesContextTestExecutionListener], listeners=[], inheritListeners=true, mergeMode=REPLACE_DEFAULTS}] for declaring class [org.springframework.test.context.testng.AbstractTestNGSpringContextTests].
[org.springframework.test.context.support.DefaultTestContextBootstrapper] - Could not instantiate TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [javax/servlet/ServletContext]
[org.springframework.test.context.support.DefaultTestContextBootstrapper] - Could not instantiate TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [javax/servlet/ServletContext]
[org.springframework.test.context.support.DefaultTestContextBootstrapper] - Using TestExecutionListeners: [org.springframework.test.context.support.DependencyInjectionTestExecutionListener@223dd567, org.springframework.test.context.support.DirtiesContextTestExecutionListener@9856ec1, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@172a45c9, org.springframework.test.context.support.DirtiesContextTestExecutionListener@584b9b00]
[org.springframework.test.context.TestContextManager] - Registering TestExecutionListener: org.springframework.test.context.support.DependencyInjectionTestExecutionListener@223dd567
[org.springframework.test.context.TestContextManager] - Registering TestExecutionListener: org.springframework.test.context.support.DirtiesContextTestExecutionListener@9856ec1
[org.springframework.test.context.TestContextManager] - Registering TestExecutionListener: org.springframework.test.context.support.DependencyInjectionTestExecutionListener@172a45c9
[org.springframework.test.context.TestContextManager] - Registering TestExecutionListener: org.springframework.test.context.support.DirtiesContextTestExecutionListener@584b9b00
[TestNG] Running:
  /private/var/folders/h1/s64jf53x51gcgns7trlqc230074rk5/T/testng-eclipse--225524899/testng-customsuite.xml

[org.springframework.test.context.TestContextManager] - beforeTestClass(): class [com.comcast.cpt.test.Experiment]
[org.springframework.test.context.TestContextManager] - prepareTestInstance(): instance [com.comcast.cpt.test.Experiment@44875666]
[org.springframework.test.context.support.DependencyInjectionTestExecutionListener] - Performing dependency injection for test context [[DefaultTestContext@19264fa9 testClass = Experiment, testInstance = com.comcast.cpt.test.Experiment@44875666, testMethod = [null], testException = [null], mergedContextConfiguration = [MergedContextConfiguration@2237ed25 testClass = Experiment, locations = '{}', classes = '{class com.comcast.cpt.test.Experiment$Configurer}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]]].
[org.springframework.test.context.support.AbstractDelegatingSmartContextLoader] - Delegating to AnnotationConfigContextLoader to load context from [MergedContextConfiguration@2237ed25 testClass = Experiment, locations = '{}', classes = '{class com.comcast.cpt.test.Experiment$Configurer}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]].
[org.springframework.test.context.support.AbstractGenericContextLoader] - Loading ApplicationContext for merged context configuration [[MergedContextConfiguration@2237ed25 testClass = Experiment, locations = '{}', classes = '{class com.comcast.cpt.test.Experiment$Configurer}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]].
[org.springframework.test.context.support.AnnotationConfigContextLoader] - Registering annotated classes: {class com.comcast.cpt.test.Experiment$Configurer}
[org.springframework.core.io.support.SpringFactoriesLoader] - Loaded [org.springframework.beans.BeanInfoFactory] names: [org.springframework.beans.ExtendedBeanInfoFactory]
[org.springframework.test.context.DefaultCacheAwareContextLoaderDelegate] - Storing ApplicationContext in cache under key [[MergedContextConfiguration@2237ed25 testClass = Experiment, locations = '{}', classes = '{class com.comcast.cpt.test.Experiment$Configurer}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]]
[org.springframework.test.context.cache] - Spring test ApplicationContext cache statistics: [ContextCache@50b7c740 size = 1, hitCount = 0, missCount = 1, parentContextCount = 0]
[org.springframework.test.context.support.DependencyInjectionTestExecutionListener] - Performing dependency injection for test context [[DefaultTestContext@19264fa9 testClass = Experiment, testInstance = com.comcast.cpt.test.Experiment@44875666, testMethod = [null], testException = [null], mergedContextConfiguration = [MergedContextConfiguration@2237ed25 testClass = Experiment, locations = '{}', classes = '{class com.comcast.cpt.test.Experiment$Configurer}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]]].
[org.springframework.test.context.DefaultCacheAwareContextLoaderDelegate] - Retrieved ApplicationContext from cache with key [[MergedContextConfiguration@2237ed25 testClass = Experiment, locations = '{}', classes = '{class com.comcast.cpt.test.Experiment$Configurer}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]]
[org.springframework.test.context.cache] - Spring test ApplicationContext cache statistics: [ContextCache@50b7c740 size = 1, hitCount = 1, missCount = 1, parentContextCount = 0]
[org.springframework.test.context.TestContextManager] - beforeTestMethod(): instance [com.comcast.cpt.test.Experiment@44875666], method [public void com.comcast.cpt.test.Experiment.aTest()]
[org.springframework.test.context.TestContextManager] - afterTestMethod(): instance [com.comcast.cpt.test.Experiment@44875666], method [public void com.comcast.cpt.test.Experiment.aTest()], exception [null]
[org.springframework.test.context.support.DirtiesContextTestExecutionListener] - After test method: context [DefaultTestContext@19264fa9 testClass = Experiment, testInstance = com.comcast.cpt.test.Experiment@44875666, testMethod = aTest@Experiment, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@2237ed25 testClass = Experiment, locations = '{}', classes = '{class com.comcast.cpt.test.Experiment$Configurer}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]], class dirties context [false], class mode [null], method dirties context [false].
[org.springframework.test.context.support.DirtiesContextTestExecutionListener] - After test method: context [DefaultTestContext@19264fa9 testClass = Experiment, testInstance = com.comcast.cpt.test.Experiment@44875666, testMethod = aTest@Experiment, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@2237ed25 testClass = Experiment, locations = '{}', classes = '{class com.comcast.cpt.test.Experiment$Configurer}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]], class dirties context [false], class mode [null], method dirties context [false].
[org.springframework.test.context.TestContextManager] - afterTestClass(): class [com.comcast.cpt.test.Experiment]
[org.springframework.test.context.support.DirtiesContextTestExecutionListener] - After test class: context [DefaultTestContext@19264fa9 testClass = Experiment, testInstance = [null], testMethod = [null], testException = [null], mergedContextConfiguration = [MergedContextConfiguration@2237ed25 testClass = Experiment, locations = '{}', classes = '{class com.comcast.cpt.test.Experiment$Configurer}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]], dirtiesContext [false].
[org.springframework.test.context.support.DirtiesContextTestExecutionListener] - After test class: context [DefaultTestContext@19264fa9 testClass = Experiment, testInstance = [null], testMethod = [null], testException = [null], mergedContextConfiguration = [MergedContextConfiguration@2237ed25 testClass = Experiment, locations = '{}', classes = '{class com.comcast.cpt.test.Experiment$Configurer}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]], dirtiesContext [false].

Analysis

In the above log output, it's clear that the listeners declared via @TestExecutionListeners on @Composed are somehow getting set to the listeners declared via @TestExecutionListeners on AbstractTestNGSpringContextTests.


Affects: 4.1 GA

Issue Links:

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Jan 24, 2015

Sam Brannen commented

Please note that this is a known shortcoming of Spring's meta-annotation support.

See #16221 for details.

In the example provided in this issue's description, @TestExecutionListeners is the inherited annotation which is currently (unintuitively) favored over @Composed which is the more locally declared composed annotation.

In other words, this is not a bug in spring-test but rather an (as of yet) unsupported feature in spring-core.

@spring-projects-issues
Copy link
Collaborator Author

Sam Brannen commented

Note: the getAnnotationAttributesFavorsInheritedAnnotationsOverMoreLocallyDeclaredComposedAnnotations() method in AnnotatedElementUtilsTests already demonstrates this shortcoming.

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Jan 24, 2015

Sam Brannen commented

Resolving this issue as a Duplicate of #16221.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: test Issues in the test module status: duplicate A duplicate of another issue
Projects
None yet
Development

No branches or pull requests

2 participants