Learn to configure and inject an in-memory DataSource instance into JUnit tests outside any Java EE container. This is a suggested solution to test or use classes that depend on container’s (for example Tomcat) JNDI environment.
In-memory DataSource instance helps in decoupling the tests from any kind of external dependency.
1. Maven Dependency
We will be using the Simple-JNDI library’s JNDI implementation which is entirely memory-based. No server instance is started. Download its latest version from Maven repository.
<dependency>
<groupId>com.github.h-thurow</groupId>
<artifactId>simple-jndi</artifactId>
<version>0.23.0</version>
</dependency>
2. Configuring JNDI Context and DataSources
To setup the initial context used by the JNDI, we need to place the jndi.properties
file in the application’s classpath. As we are using this feature for unit testing, we can place the file in 'src/test/resources'
directory.
java.naming.factory.initial=org.osjava.sj.SimpleContextFactory
org.osjava.sj.jndi.shared=true
jndi.syntax.separator=/
org.osjava.sj.space=java:/comp/env
org.osjava.sj.root=src/test/resources/jndi
- java.naming.factory.initial is a part of the JNDI specification and specifies the context factory class used to create the initial context. It is a mandatory property.
- org.osjava.sj.root is another mandatory property and points to the directory location where we store the files used to define the context objects.
- org.osjava.sj.jndi.shared controls if all InitialContext objects will share the same memory.
- org.osjava.sj.space – Its value is prepended to every value loaded into the system. Thus org.osjava.sj.space=java:comp/env simulates the JNDI environment of Tomcat.
Now we need to create one or multiple instances of a DataSource. So, create a property file in the directory location as configured in org.osjava.sj.root
.
Simple-JNDI will load all the property files in this location. We have created one such file, datasource.properties. Here we are creating a DataSource for H2 database.
ds.type=javax.sql.DataSource
ds.driver=org.h2.Driver
ds.url=jdbc:h2:mem:testdb
ds.user=sa
ds.password=
3. Initializing DataSource in Unit Tests
To initialize a DataSource instance, start with creating an InitialContext
object and lookup the Context using java:/comp/env
(as specified in ‘org.osjava.sj.space’ value).
Using the Context, we can lookup datasources as configured in datasource.properties. And finally, we can use the DataSource instance further in the unit tests.
public class TestMockDataSource {
private static InitialContext initContext;
@BeforeAll
public static void setup() throws Exception {
initContext = new InitialContext();
}
@Test
public void whenMockJndiDataSource_thenReturnJndiDataSource() throws Exception {
Context envContext = (Context) this.initContext.lookup("java:/comp/env");
DataSource ds = (DataSource) envContext.lookup("datasource/ds");
Assertions.assertEquals(
"org.h2.Driver::::jdbc:h2:mem:testdb::::sa", ds.toString());
//Use the DataSource as needed
Connection conn = ds.getConnection();
Assertions.assertNotNull(conn);
}
@AfterAll
public static void tearDown() throws Exception {
initContext.close();
}
}
Do not forget to close the datasources at the end.
4. Conclusion
In this short tutorial, we learned to create and inject an in-memory DataSource instance that can be used to mock J2EE container provided JNDI DataSource instance.
Happy Learning !!