javajunitjndi

Junit Testing JNDI InitialContext outside the application server


Context context = new InitialContext();
dataSource = (DataSource) context.lookup("java:comp/env/jdbc/multiDS");
connection = dataSource.getConnection();

Please help me to mock the above code.

Hi Tom Anderson

I tried the below code

 @BeforeClass
 public static void setUpClass() throws Exception {
        // rcarver - setup the jndi context and the datasource
        try {
            // Create initial context
            System.setProperty(Context.INITIAL_CONTEXT_FACTORY,
                "org.apache.naming.java.javaURLContextFactory");
            System.setProperty(Context.URL_PKG_PREFIXES, 
                "org.apache.naming");            
            Context ic = new InitialContext();

            ic.createSubcontext("java:");
            ic.createSubcontext("java:comp");
            ic.createSubcontext("java:comp/env");
            ic.createSubcontext("java:comp/env/jdbc");
            ic.createSubcontext("java:comp/env/jdbc/multiDS");
            // Construct DataSource
            OracleConnectionPoolDataSource ds = new OracleConnectionPoolDataSource();
            ds.setURL("jdbc:oracle:thin:@g9u0696.houston.hp.com:1525:CRNAD");
            ds.setUser("uname");
            ds.setPassword("pwd");
        } catch (NamingException ex) {
            ex.printStackTrace();
        }
 }

But its giving error as:

com.hp.corona.common.exception.CacheException: org.apache.naming.NamingContext cannot be cast to javax.sql.DataSource

Please help me to test the code i just want connection from JNDI datasource


Solution

  • 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
        }
    }