Create JNDI resources for JUnit testing using Spring

By on

Until recently, I had static methods setting up my in memory database (HSQLDB). I called these methods in setUp/tearDown of my JUnit tests. This felt always a bit unnatural to me as I use Spring and everything should run through it's application context.

JNDI with Spring

Creating a simple JNDI bean

As I use JNDI in production, I had to created a JNDI resource for my application as well. A simple Spring Bean using Apache Commons DBCP does the trick:

public class JndiBean {

    public JndiBean() {
        try {
            DriverAdapterCPDS cpds = new DriverAdapterCPDS();
            cpds.setDriver("org.hsqldb.jdbc.JDBCDriver");
            cpds.setUrl("jdbc:hsqldb:mem:testdb");
            cpds.setUser("SA");
            cpds.setPassword("");

            SharedPoolDataSource dataSource = new SharedPoolDataSource();
            dataSource.setConnectionPoolDataSource(cpds);
            dataSource.setMaxActive(10);
            dataSource.setMaxWait(50);

            SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();
            builder.bind("java:comp/env/jdbc/timeandbill", dataSource);
            builder.activate();
        } catch (NamingException | ClassNotFoundException ex) {
            ex.printStackTrace();
        }
    }
}

You should not use this basic configuration without thinking twice, but it works well for unit testing.

First, I created a driver adapter, containing whatever I need for connecting to my database. It could be MySQL, Postgres or whatever else you prefer.

Then I create a SharedPoolDatasource. It is not really necessary to keep up a lot of connections, as tests usually run sequential. Even if not, the Spring context usually will be created at least per test class. It is unlikely you get out a benefit of pooling here, but I wanted to stick to what production servers usually do.

The SimpleNamingContextBuilder is what eventually bind the perviously created data source to the JNDI context. As you see, it's a straightforward thing todo: just bind it, then activate and you are done.

Adding the JNDI bean to the context

The next step is to add this to a second applicationContext.xml, which is loaded by JUnit tests only. I resides in my Unit-tests folder and contains:

<bean id="jndi" class="de.grobmeier.tab.webapp.JndiBean" lazy-init="false" />

These annotations on my tests make sure I load all applicationContext files:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath*:applicationContext.xml"})
public class TimeConverterTest {

My production applicationContext contains this:

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="java:comp/env/jdbc/timeandbill"/>
    <property name="resourceRef" value="true" />
</bean>

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
</bean>

As there is no Java EE server running at JUnit level. Instead the JNDI connection was created manually. In production, the JUnits applicationContext is not loaded and the Java EE container provides the JNDI resource.

Side note: I found Mybatis relies on Springs autowire "byType" feature.

Tags: java, spring, junit, jndi, java ee