Java 如何将 DataSource 绑定到 InitialContext 以进行 JUnit 测试?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3461310/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 00:59:00  来源:igfitidea点击:

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

javajdbcjunitglassfishdatasource

提问by Rambo Commando

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

我正在尝试在数据库“worker”类上运行 JUnit 测试,这些类在 an 上执行 jndi 查找InitialContext以获取DataSource. 工作类通常运行在 Glassfish v3 应用服务器上,它定义了适当的 jdbc 资源。

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.

代码部署在 App Server 上运行得很好,但不能在 JUnit 测试环境中运行,因为它显然找不到 jndi 资源。所以我试图在测试类中设置一个 InitialContext 将数据源绑定到适当的上下文,但它不起作用。

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 DataWorkerclass has a method with the following code, more or less

然后这个DataWorker类有一个方法,代码如下,或多或少

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".

如果我改变
ic.createSubContext("java:/comp/env/jdbc");
ic.bind("java:/comp/env/jdbc/TestDS",testDS);
线
ic.createSubContext("jdbc");
ic.bind("jdbc/TestDS",testDS);
工人阶级是能够找到数据源,但未能给出一个错误,指出“用户名无法登录到服务器”。

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

如果我将在 JUnit 方法中创建的 DataSource 直接传递给 worker,它可以连接并运行查询。

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.

所以,我想知道如何绑定一个可以被worker类查找而无需在Web Container中的DataSource。

回答by Rodney Gitzel

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.

仍然,这闻起来......你不应该有任何直接依赖于数据源或 JNDI 查找等的“业务逻辑”代码。这就是在您的代码之外连接在一起的所有管道。

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.

你的设计有多灵活?如果您的被测代码直接依赖于 DataSource(甚至获得自己的 Connection),请对其进行重构。注入连接将使您可以使用普通的旧 JDBC 测试您喜欢的所有内容,甚至使用内存中的实现,并使您不必支持许多不必要的(无论如何,对于测试)基础结构来执行此操作。

回答by MarcD

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.

您会注意到的不同之处在于,每个上下文中“java:”之后的“/”都是错误的,不应该出现在那里。