Description
Andy Wilkinson opened SPR-16595 and commented
This feels like a bug, although it may just be a (rather large) limitation.
It appears to be impossible for an inner test class annotated with @Nested
to use the enclosing class's application context if the enclosing class is deemed to be a configuration candidate (for example, because it uses @Import
).
To be able to share the context, the inner test class must have the same configuration as its enclosing class so that they have the same context cache key. In other words, if the enclosing class has used @Import
the inner class must do so too. This leads to code like this:
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ExtendWith(SpringExtension.class)
@Import(Imported.class)
class NestedLimitationsTests {
@Nested
@Import(Imported.class)
class NestedTests {
@Test
public void test() {
}
}
}
class Imported {
}
This fails to launch with the following exception:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'example.NestedLimitationsTests$NestedTests': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'example.NestedLimitationsTests' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:729)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:192)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1270)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1127)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:545)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:502)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:312)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:310)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:760)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:868)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549)
at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:128)
at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:60)
at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:107)
at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:251)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:117)
... 88 common frames omitted
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'example.NestedLimitationsTests' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1509)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1104)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1065)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:815)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:721)
... 106 common frames omitted
An attempt is being made to create a NestedTests
bean as its @Import
annotation means that ConfigurationClassUtils
considers it to be a configuration candidate. An instance of the enclosing class is needed to create NestedTests
as the class is not static. It fails because the enclosing class isn't available as a bean.
Declaring the inner class as static
overcomes the immediate problem but creates another. Being static prevents the inner class from accessing the enclosing class's non-static fields and declaring the enclosing class's fields as static means that they're no longer autowired.
The situation that's described above means that many of Spring Boot's testing annotations do not work. In fact, as far as I can tell, the only one that does work is @SpringBootTest
as it is not meta-annotated with @Import
.
Affects: 5.0.4
Reference URL: spring-projects/spring-restdocs#490
Issue Links:
- Discover test configuration on enclosing class for nested test class [SPR-15366] #19930 Discover test configuration on enclosing class for nested test class
- Revisit @Bean introspection between @Configuration classes and 'lite' beans [SPR-17206] #21739 Revisit
@Bean
introspection between@Configuration
classes and 'lite' beans
Referenced from: commits spring-attic/spring-framework-issues@7a3db4a
1 votes, 6 watchers