This post originated from an RSS feed registered with Java Buzz
by Marc Logemann.
Original Post: The joy of JNDI mocking / testing
Feed Title: Logemann Blog
Feed URL: http://www.logemann.org/blojsom/blog/default/?flavor=rss2
Feed Description: Marc's thoughts on Java and more
Lately i was writing unit tests for code that uses datasources looked up via JNDI. So i simlpy wrote a Mock for jndi lookups like this:
{code}
public class CtxMock implements Context {
public static class Factory
implements InitialContextFactory {
private static Context ctx;
public Context getInitialContext(Hashtable environment)
throws NamingException {
return ctx;
}
protected static void setContext(Context context) {
ctx = context;
}
}
Map objects = new HashMap();
public CtxMock() {
System.setProperty(Context.INITIAL_CONTEXT_FACTORY,
Factory.class.getName());
Factory.setContext(this);
}
public Object lookup(String name) throws NamingException {
return objects.get(name);
}
public void bind(String name, Object obj) throws NamingException {
objects.put(name, obj);
}
public void unbind(String name) throws NamingException {
objects.remove(name);
}
[..]
{code}
This works pretty well. In a test case you only need to create a CtxMock instance and bind values to the Context. Later on in the production code test, each call to "new InitialContext()" will be handled by the CtxMock because it defined the Environment variable "java.naming.factory.initial" apropriately.
Unfortunately i needed to bind a full jdni name like "java:comp/..." to the context. This in turn resulted in the "normal" resolving rules as defined by Sun so that InitialContext looks first for URI resolvers like javaUrlContextFactory.class. Sadly this class is in the classpath because we need to have some websphere classes in the test classpath including naming.jar. All this prevented my CtxMock to be used when doing jndi lookups().
The solution is a little bit scary but it works. You simply need to create your own InitialContextFactoryBuilder and register it with the NamingManager via NamingManager.setInitialContextFactoryBuilder(). When doing this, you have full control over the process how the jndi lookup works.
Here is an example:
{code}
public class MyContextFactoryBuilder implements
InitialContextFactoryBuilder {
static CtxMock jndiMock = new CtxMock();
public InitialContextFactory createInitialContextFactory(
Hashtable environment) throws NamingException {
return new InitialContextFactory() {
public Context getInitialContext(Hashtable environment)
throws NamingException {
return ctxMock;
}
};
}
}
{code}
Remember, in your testcase you definitely need to register your new InitialContextFactoryBuilder via the described method.
If you just want to place simple names in the JDNI context like "jdbc/datasource1" or if you dont have any naming jars in the classpath containg the URI contextFactory classes. You can simply use CtxMock in your testclasses like this:
{code}
CtxMock ctx = new CtxMock();
ctx.bind(CacheDB.DATASOURCE_NAME, new DerbyMockDataSource());
{code}
If you must use the more difficult path, you need to do this:
{code}
// one time only during JVM session
NamingManager.setInitialContextFactoryBuilder(new MyContextFactoryBuilder());
Context ctx = NamingManager.getInitialContext(null);
ctx.bind(CacheDB.DATASOURCE_NAME, new DerbyMockDataSource());
{code}