Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I bind a DataSource to an InitialContext for JUnit testing? [duplicate]

I'm trying to run JUnit tests on database "worker" classes that do a jndi lookup on an InitialContext to get a DataSource. The worker classes are usually running on a Glassfish v3 App Server which has the appropriate jdbc resources defined.

The code runs just fine when deployed on the App Server but doesn't run from the JUnit testing environment, because obviously it can't find the jndi resources. So I tried to setup an InitialContext in the test class that binds a datasource to the appropriate context, but it doesn't work.

Here is the code I have in the test

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

        ic.createSubcontext("java:");
        ic.createSubcontext("java:/comp");
        ic.createSubcontext("java:/comp/env");
        ic.createSubcontext("java:/comp/env/jdbc");

        // Construct DataSource
        SQLServerConnectionPoolDataSource testDS = new SQLServerConnectionPoolDataSource();
        testDS.setServerName("sqlserveraddress");
        testDS.setPortNumber(1433);
        testDS.setDatabaseName("dbname");
        testDS.setUser("username");
        testDS.setPassword("password");

        ic.bind("java:/comp/env/jdbc/TestDS", testDS);

        DataWorker dw = DataWorker.getInstance();
    } catch (NamingException ex) {
        Logger.getLogger(TitleTest.class.getName()).log(Level.SEVERE, null, ex);
    }
}

Then the DataWorker class has a method with the following code, more or less

InitialContext ic = null;
DataSource ds = null;
Connection c = null;
PreparedStatement ps = null;
ResultSet rs = null;
String sql = "SELECT column FROM table";
try{
    ic = new InitialContext();
    ds = (DataSource) ic.lookup("jdbc/TestDS");
    c = ds.getConnection();
    ps = c.prepareStatement(sql);
    // Setup the Prepared Statement
    rs = ps.executeQuery();
    if(rs.next){
        //Process Results
    }
}catch(NamingException e){
    throw new RuntimeException(e);
}finally{
    //Close the ResultSet, PreparedStatement, Connection, InitialContext
}

If I change the
ic.createSubContext("java:/comp/env/jdbc");
ic.bind("java:/comp/env/jdbc/TestDS",testDS);
lines to
ic.createSubContext("jdbc");
ic.bind("jdbc/TestDS",testDS);
The worker class is able to find the DataSource, but fails giving an error saying that "username failed to login to the server".

If I pass the DataSource that I create in the JUnit method directly into the worker, it can connect and run queries.

So, I would like to know how to bind a DataSource that can be looked up by the worker class without being in the Web Container.

like image 325
Brad H. Avatar asked Aug 11 '10 17:08

Brad H.


2 Answers

When I last tried something like this a few years ago, I finally gave up and refactored: at that point, you could NOT create a DataSource outside of a container. Maybe you can, now, maybe someone's mocked something up.

Still, that smells... You shouldn't have ANY "business logic" code directly dependent on DataSources or JNDI lookups or such. That's all plumbing to be wired together outside your code.

How flexible is your design? If your code under test is directly dependent on a DataSource (or even obtains its own Connection), refactor it. Injecting a Connection will let you can test all you like with plain old JDBC, even using an in-memory implementation, and save you from having to prop up a lot of unnecessary (for the test, anyway) infrastructure to do it.

like image 77
Rodney Gitzel Avatar answered Oct 08 '22 01:10

Rodney Gitzel


I found that example to be wrong as well. This worked for me.

ic.createSubcontext("java:comp");
ic.createSubcontext("java:comp/env");
ic.createSubcontext("java:comp/env/jdbc");

final PGSimpleDataSource ds = new PGSimpleDataSource();
ds.setUrl("jdbc:postgresql://localhost:5432/mydb");
ds.setUser("postgres");
ds.setPassword("pg");

ic.bind("java:comp/env/jdbc/mydb", ds);

The difference you'll note is that the '/' after 'java:' in each of the contexts is wrong and shouldn't be there.

like image 24
MarcD Avatar answered Oct 08 '22 02:10

MarcD