Technically speaking, an automated test that requires JNDI is not a unit test. As an aside, it is preferrable to segregate the portions of the application accessing JNDI to leave as much of the application unit testing as possible. Nevertheless, if JNDI is used, something must ultimately do it and it is preferrable to be able to test this code prior to its us in production.
So far, the best starting point for me has been to use Simple JNDI to create the provider and allow the rest of the code to work unimpeded. The original Simple JNDI project decayed a little bit. An update with bug fixes is available on github with a different group id (https://github.com/h-thurow/Simple-JNDI). I also used H2's in-memory database so that I could put real connection information in the test case.
To get started, I added these dependencies to my pom.xml:
com.github.h-thurow
simple-jndi
0.12.0
test
com.h2database
h2
1.4.192
test
It took some trial and error to get the configuration right, which is one of the motivations for this writeup. The first thing you need is to add a jndi.properties
file to your test resources. The settings I will discuss below were chosen to emulate the Tomcat Server setup that we use here.
java.naming.factory.initial = org.osjava.sj.SimpleContextFactory
org.osjava.sj.root=target/test-classes/config/
org.osjava.sj.space=java:/comp
#org.osjava.sj.jndi.shared=true
org.osjava.sj.delimiter=/
Notice that the root is given relative to the pom. This is one thing that caused me a great deal of grief on the first pass. Another important element was the use of the space option, which was needed to emulate Tomcat's environment. Within the test resources, I added a config
folder, containing a single file: env.properties
. This latter file had contents like the following:
org.example.mydatasource/type=javax.sql.DataSource
org.example.mydatasource/url=jdbc:h2:mem:
org.example.mydatasource/driver=org.h2.Driver
org.example.mydatasource/user=
org.example.mydatasource/password=
The data source in question did indeed have a dotted name, which only added to some of the initial confusion. This allowed my code-under-test to work as anticipated. For reference's sake, this is what the Java code looked like:
private DataSource retrieveDataSource() {
try {
Context initContext = new InitialContext();
Context envContext = (Context)initContext.lookup("java:/comp/env");
return (DataSource)envContext.lookup(DATA_SOURCE_NAME);
} catch(NamingException e) {
log.error("Error attempting to find data source", e);
return null;
}
}
Sources
- https://web.archive.org/web/20140530014804/https://blogs.oracle.com/randystuph/entry/injecting_jndi_datasources_for_junit
- https://github.com/h-thurow/Simple-JNDI
- https://tomcat.apache.org/tomcat-7.0-doc/jndi-resources-howto.html
- http://stackoverflow.com/questions/2324937/code-to-list-all-the-entries-in-jndi-on-remote-machine