We want to be able to run integration tests against our production code without always having to deploy to an app server. Once the production code uses JNDI to get the JTA TransactionManager* and JMS and maybe Hibernate connection factories, how are we going to do those lookups without running on the app server?
*or is it UserTransaction — I haven’t read the JTA spec, so I’m not sure what magic Spring is doing to connect to the application server’s XA transaction manager.
There’s a paragraph in the Spring 2.5 Manual’s Testing chapter that talks about exactly the issue we’re discussing:
As far as possible, you should have exactly the same Spring configuration files in your integration tests as in the deployed environment. One likely point of difference concerns database connection pooling and transaction infrastructure. If you are deploying to a full-blown application server, you will probably use its connection pool (available through JNDI) and JTA implementation. Thus in production you will use a JndiObjectFactoryBean for the DataSource and JtaTransactionManager. JNDI and JTA will not be available in out-of-container integration tests, so you should use a combination like the Commons DBCP BasicDataSource and DataSourceTransactionManager or HibernateTransactionManager for them. You can factor out this variant behavior into a single XML file, having the choice between application server and ‘local’ configuration separated from all other configuration, which will not vary between the test and production environments. In addition, it is advisable to use properties files for connection settings: see the PetClinic application for an example.
That’s pretty on-point there!
Another alternative is to have a bare-bones JNDI provider that gives your integration test the objects it needs. Earlier in the Testing chapter, the Spring manual points us to the org.springframework.mock.jndi package, which provides an ExpectedLookupTemplate for this purpose. ExpectedLookupTemplate extends JndiTemplate and lets you pass to the constructor a JNDI name and an object to return. It appears that you could use an ExpectedLookupTemplate in your Spring bean file so that instead of just this:
<jee:jndi-lookup id="queueConnectionFactory" jndi-name="java:activemq/QueueConnectionFactory" />
you’d add this:
<bean id="pooledConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory"> <constructor-arg index="0" value="vm://localhost" /> </bean> <bean id="fakeJndiServer" class="org.springframework.mock.jndi.ExpectedLookupTemplate"> <constructor-arg index="0" value="java:activemq/QueueConnectionFactory" /> <constructor-arg index="1" ref="pooledConnectionFactory" /> </bean>
And then users of the queueConnectionFactory bean could go on their merry way, not needing to know where the queueConnectionFactory came from.
You’d also need to add this to your pom.xml file:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-mock</artifactId> <version>2.0.8</version> </dependency>
And then all the rest of your Java code and configuration files could be the same.
Update 8/11/2008: The above example is not quite right. See Part 2 for a working example of mocking JNDI.