Context context = new InitialContext();
dataSource = (DataSource) context.lookup(\"java:comp/env/jdbc/multiDS\");
connection = dataSource.getConnection();
The orthodox thing to do here would be to change you code so that the Context
is injected into it (by a dependency injection framework, or manually). Then, you simply pass in a mock in your unit test.
If you can't do this, and your code must create the IntialContext
itself, then you will need to set up a fake JNDI implementation into which you can inject mocks. If you search the web for in-memory JNDI implementation or mock JNDI implementation, you will find various options, or you could write one yourself. Basically, you will need an implementation of InitialContextFactory
which simply returns a suitable mock, which you then select by setting the java.naming.factory.initial
system property.
I had a crack at writing the necessary classes. Here you go:
public class MockInitialContextFactory implements InitialContextFactory {
private static final ThreadLocal<Context> currentContext = new ThreadLocal<Context>();
@Override
public Context getInitialContext(Hashtable<?, ?> environment) throws NamingException {
return currentContext.get();
}
public static void setCurrentContext(Context context) {
currentContext.set(context);
}
public static void clearCurrentContext() {
currentContext.remove();
}
}
public class MockInitialContextRule implements TestRule {
private final Context context;
public MockInitialContextRule(Context context) {
this.context = context;
}
@Override
public Statement apply(final Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, MockInitialContextFactory.class.getName());
MockInitialContextFactory.setCurrentContext(context);
try {
base.evaluate();
} finally {
System.clearProperty(Context.INITIAL_CONTEXT_FACTORY);
MockInitialContextFactory.clearCurrentContext();
}
}
};
}
}
Use as follows:
public class FooTest {
private final Context context = mock(Context.class);
@Rule
public MockInitialContextRule mockInitialContextRule = new MockInitialContextRule(context);
@Test
public void testName() throws Exception {
// set up stubbings on the context mock
// invoke the code under test
}
}
You can use the Spring implementation, it works like this:
import org.springframework.mock.jndi.SimpleNamingContextBuilder;
[...]
SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();
builder.bind("jdbc/myDataSource", myDS);
builder.activate();
This is easily done with Simple-JNDI. Create a property file "jdbc/multiDS.properties" in your working directory to configure your datasource with these properties:
type=javax.sql.DataSource
driver=org.gjt.mm.mysql.Driver
url=jdbc:mysql://localhost/testdb
user=testuser
password=testing
Then instantiate the context with
final Hashtable<String, String> env = new Hashtable<String, String>();
env.put("org.osjava.sj.root", "working_dir");
env.put("org.osjava.sj.jndi.shared", "true");
env.put("java.naming.factory.initial", "org.osjava.sj.SimpleContextFactory");
env.put("org.osjava.sj.delimiter", "/");
env.put("org.osjava.sj.space", "java:comp/env")
Context ctx = new InitialContext(env);
After that you can call
dataSource = (DataSource) context.lookup("java:comp/env/jdbc/multiDS");
Find more info about Simple-JNDI here https://github.com/h-thurow/Simple-JNDI