27
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

Tom Anderson
  • 46,189
  • 17
  • 92
  • 133
ravichandra
  • 273
  • 1
  • 4
  • 7
  • it really depends on your app server . any ways take a look at this http://www.codeproject.com/Articles/25741/EJB-3-0-Remote-Session-Bean-Lookup – fGo Jun 13 '13 at 09:08

4 Answers4

28

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
    }
}
Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315
Tom Anderson
  • 46,189
  • 17
  • 92
  • 133
  • Hi Mr Anderson.. i Just edited the testcode please give me suggestion – ravichandra Jun 13 '13 at 10:18
  • Your code isn't right. Compare it to Randy Carver's original code: https://blogs.oracle.com/randystuph/entry/injecting_jndi_datasources_for_junit . You are doing a `createSubcontext` when you should be doing a `bind`. The exception message is hinting at that - you have created a `NamingContext` where you should have created a `DataSource`. – Tom Anderson Jun 13 '13 at 15:24
  • Hi Mr Anderson i dont know about JNDI .. so not getting what you suggesting me please help me to correct the code – ravichandra Jun 17 '13 at 09:17
  • 2
    You should do two things. Firstly, learn the basics of JNDI, perhaps by reading the [JNDI trail of the Java tutorial](http://docs.oracle.com/javase/tutorial/jndi/index.html). How can you hope to do this work without understanding JNDI? Secondly, as i said, compare your code with [the original version of you have copied](https://blogs.oracle.com/randystuph/entry/injecting_jndi_datasources_for_junit). There is a difference which you have added which is the cause of the problem. – Tom Anderson Jun 17 '13 at 15:21
  • 1
    Hi Mr Anderson i corrected the testcode by comparing ...its working Thanks a lot. – ravichandra Jun 18 '13 at 06:39
  • I think the same question is answered pretty clearly [here](http://stackoverflow.com/a/10047414/606583) – soltysh Nov 13 '13 at 10:36
  • @soltysh: That is indeed basically the same idea. My version has the sweet `TestRule`, though. – Tom Anderson Nov 13 '13 at 12:21
  • thank you @TomAnderson this was really very helpful. – Arun Pratap Singh Sep 25 '17 at 06:09
  • why MockInitialContextRule constructor is private? – TimeTraveler Nov 28 '17 at 18:06
  • @TimeTraveler I think that's a mistake - i'll make it public. Thanks for catching it! – Tom Anderson Nov 29 '17 at 11:11
8

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();
Michael Böckling
  • 7,341
  • 6
  • 55
  • 76
2

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

Holger Thurow
  • 764
  • 4
  • 12
  • If you're using Tomcat as your container and doing all this to try and test some code you normally run in Tomcat, check out TomcatJNDI that Simple-JNDI points to. – jla Dec 27 '18 at 02:07
0

I had problems using the suggested solutions on a legacy system I am adding tests to, but I have found that using Mockito.mockConstruction can be a simple approach:

public class FooTest {
  private final Context mockContext = mock(Context.class);

  @Test
  public void myTest()
      throws NamingException {

    try (final MockedConstruction<InitialContext> ignored = 
               mockConstruction(InitialContext.class,
               (mock, context) -> 
                    when(mock.lookup("java:comp/env"))
                    .thenReturn(mockContext)) {
    // Given
    when(mockContext.lookup("environmentEntry"))
        .thenReturn(desiredResult);
    ...
  }
}
Steve Bosman
  • 2,599
  • 1
  • 25
  • 41