David M. Karr opened SPR-15990 and commented

I wanted to write a unit test that just verifies that using my default application context XML file, that expected bindings are done. The app runs in a container with JNDI, where some external resources are defined. In my unit test I had to set those to dummy values. I figured the best place to set those dummy values would be in a @Configuration class defined inline in my unit test class, as I would have to have tests in the same file that checked against those same dummy values. This is as opposed to defining a "testResources.xml" file outside of the unit test that defines the dummy values. This would have been suboptimal to me, as I would have to write tests to check beans against hardcoded values which were originally set in that external file. Having the internal JavaConfig class would be better.

Note that I've already posted a question about this to StackOverflow, and the only authoritative answer I got was describing the structure I show here, which doesn't work.

In any case, it seemed to me that something like this is what I needed:

@RunWith(SpringRunner.class)
@Configuration
public class SpringWiringTest {
    ...
    @Configuration
    @ImportResource("file:src/main/webapp/WEB-INF/applicationContext.xml")
    public static class Config {
        @Bean public String uslDatasourcesList() { return "abc"; }
        @Bean public String atgDatasourcesList() { return "abc"; }
        @Bean public String uslTableNamePrefixsList() { return "abc"; }
        @Bean public String atgTableNamePrefixsList() { return "abc"; }
        @Bean public String doNotifications() { return "false"; }
        @Bean public DataSource abc() { return new DriverManagerDataSource(); }
    }

Here are lines in the application context that reference these beans:

<jee:jndi-lookup jndi-name="uslDatasourcesList" id="uslDatasourcesList"/>
<jee:jndi-lookup jndi-name="atgDatasourcesList" id="atgDatasourcesList"/>
<jee:jndi-lookup jndi-name="uslTableNamePrefixList" id="uslTableNamePrefixList"/>
<jee:jndi-lookup jndi-name="atgTableNamePrefixList" id="atgTableNamePrefixList"/>
<jee:jndi-lookup jndi-name="doNotifications" id="doNotifications"/>

Note that I also have this in my test class:

@BeforeClass
public static void setup() throws Exception {
    SimpleNamingContextBuilder  builder = SimpleNamingContextBuilder.emptyActivatedContextBuilder();
    DataSource  ds  = new DriverManagerDataSource();
    builder.bind("java:comp/env/abc", ds);
}

When I run this test class, I get the following:

java.lang.IllegalStateException: Failed to load ApplicationContext
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
    at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:117)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:230)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:228)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:287)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:289)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:247)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'uslDatasourcesList': Invocation of init method failed; nested exception is javax.naming.NameNotFoundException: Name [uslDatasourcesList] not bound; 1 bindings: [java:comp/env/abc]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1628)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:742)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)
    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:108)
    at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:251)
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98)
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116)
    ... 26 more
Caused by: javax.naming.NameNotFoundException: Name [uslDatasourcesList] not bound; 1 bindings: [java:comp/env/abc]
    at org.springframework.mock.jndi.SimpleNamingContext.lookup(SimpleNamingContext.java:137)
    at javax.naming.InitialContext.lookup(Unknown Source)
    at org.springframework.jndi.JndiTemplate$1.doInContext(JndiTemplate.java:155)
    at org.springframework.jndi.JndiTemplate.execute(JndiTemplate.java:87)
    at org.springframework.jndi.JndiTemplate.lookup(JndiTemplate.java:152)
    at org.springframework.jndi.JndiTemplate.lookup(JndiTemplate.java:179)
    at org.springframework.jndi.JndiLocatorSupport.lookup(JndiLocatorSupport.java:104)
    at org.springframework.jndi.JndiObjectLocator.lookup(JndiObjectLocator.java:106)
    at org.springframework.jndi.JndiObjectFactoryBean.lookupWithFallback(JndiObjectFactoryBean.java:231)
    at org.springframework.jndi.JndiObjectFactoryBean.afterPropertiesSet(JndiObjectFactoryBean.java:217)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624)
    ... 41 more

So, I then replaced the empty @ContextConfiguration at the top of the test class with this:

@ContextConfiguration(value = {"file:src/main/webapp/WEB-INF/applicationContext.xml", "/testResources.xml"})

And this being the meat of "testResources.xml":

<bean id="uslDatasourcesList" class="java.lang.String"> <constructor-arg value="abc"/> </bean>
<bean id="atgDatasourcesList" class="java.lang.String"> <constructor-arg value="abc"/> </bean>
<bean id="uslTableNamePrefixList" class="java.lang.String"> <constructor-arg value="abc"/> </bean>
<bean id="atgTableNamePrefixList" class="java.lang.String"> <constructor-arg value="abc"/> </bean>
<bean id="doNotifications" class="java.lang.String"> <constructor-arg value="false"/> </bean>
<bean id="abc" class="org.springframework.jdbc.datasource.DriverManagerDataSource"></bean>

And I commented out the internal JavaConfig class. This all worked fine. However, it's not what I want, as I described earlier.


Affects: 4.3.11

Comment From: snicoll

There is a lifecycle issue here. You said that the first example had an empty ContextConfiguration but your code snippet doesn't. This is the problem with code snippet in text as they're might be missing details that you only know and that are important for us to reproduce the issue. Back to the original problem, It is possible for beans to refer to each other as the context produces an intermediate bean definitions set.

Unfortunately, there's not much I can do based on the description so I am going to close this, but we can reopen if you share a sample we can run ourselves.