Java 如何为jdbc模拟上下文数据源

Java 如何为jdbc模拟上下文数据源,java,jdbc,mocking,junit4,Java,Jdbc,Mocking,Junit4,我有这段代码来连接数据库,它是由Strutsapplication定义的 public void initConnection() { if (this.con == null) { try { Context ctx = new InitialContext(); Context envContext = (Context)ctx.lookup("java:comp/env"); this.ds

我有这段代码来连接数据库,它是由Strutsapplication定义的

public void initConnection()
   {
     if (this.con == null) {
       try
       {
         Context ctx = new InitialContext();
         Context envContext = (Context)ctx.lookup("java:comp/env");
         this.ds = ((DataSource)envContext.lookup("jdbc/FooBar"));
         if (this.ds != null)
         {
           this.con = this.ds.getConnection();
           if (this.con != null) {
             LOGGER.info("datasbase connection established");
           } else {
             LOGGER.error("there was an error during connectiong to the database");
           }
         }
       }
       catch (Exception e)
       {
         LOGGER.error(e);
       }
     }
   }
现在,上下文是使用Context.xml定义的

<Context>
    <Resource name="jdbc/FooBar" auth="Container" type="javax.sql.DataSource"
        maxActive="20" maxIdle="30" maxWait="10000" username="root" password=""
        driverClassName="com.mysql.jdbc.Driver"
        url="jdbc:mysql://localhost:3306/ProductiveDB?autoReconnect=true" />
</Context>
然后用

new InitialContext().addToContext("jdbc/FooBar",resource);
编辑II: 我在设置函数中编辑了代码,如下所示:

ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/FooBar");
ds.setUsername("root");
ds.setPassword("");
Context ctx = new InitialContext();
System.out.println(ctx);
System.out.println(ctx.getEnvironment());
ctx.addToEnvironment("java:comp/env/jdbc/FooBar", ds);
new Expectations() {
    InitialContext ctx;
    {
        ...
        ctx = new InitialContext(); // expect constructor
        ctx.lookup(anyString); result = myMockThingie;
        ...
    }
}
我认为,通过这种方式,我更接近我的解决方案,但现在我得到了以下错误:

javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file:  java.naming.factory.initial
at javax.naming.spi.NamingManager.getInitialContext(Unknown Source)
at javax.naming.InitialContext.getDefaultInitCtx(Unknown Source)
at javax.naming.InitialContext.getEnvironment(Unknown Source)
at com.foo.bar.su.tests.StepDefs.prepareTests(StepDefs.java:55)
at com.foo.bar.su.tests.HomeTest.setUp(HomeTest.java:22)
我可以看出,这个错误显然意味着,我的环境。找不到。但是我怎样才能“创建”它,或者其他什么呢?

我使用JMockit(我最喜欢的模拟工具包)来实现这一点

@Mocked(stubOutClassInitialization=true) final InitialContext unused = null;
编辑1: 在另一个例子中,我有这样的东西:

ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/FooBar");
ds.setUsername("root");
ds.setPassword("");
Context ctx = new InitialContext();
System.out.println(ctx);
System.out.println(ctx.getEnvironment());
ctx.addToEnvironment("java:comp/env/jdbc/FooBar", ds);
new Expectations() {
    InitialContext ctx;
    {
        ...
        ctx = new InitialContext(); // expect constructor
        ctx.lookup(anyString); result = myMockThingie;
        ...
    }
}
在我的例子中,我对连接到备用数据库并不特别感兴趣,因此这并不完全是您想要的情况,尽管根据您的需要进行定制似乎很容易

编辑2:
再想一想,也许你可以在这里完全避开嘲笑。为什么不为了测试的目的,创建测试主题的子类并重写initConnection()方法以使用所需的连接呢?我经常使用这种技巧,也许这对你来说是最简单的。请注意,您可以在测试类中创建一个静态内部类,该类对测试主题进行子类化,因此不必担心生产代码中会出现专门用于测试的工件。希望有帮助

您应该避免以编程方式查找,因为这会使代码更难测试。最好使用一个框架来控制依赖注入。然后您可以使用类似DBUnit的东西

如果不能使用依赖注入,您仍然可以使代码更易于测试。例如,可以按如下方式隔离数据源查找:

public abstract class MyAbstractDao {
    private DataSource ds = null;

    protected DataSource getDataSource() {
        if (ds == null) {
            Context ctx = new InitialContext();
            Context envContext = (Context)ctx.lookup("java:comp/env");
            return (DataSource)envContext.lookup("jdbc/FooBar");    
        }
        return ds;
    }

    public void setDataSource(DataSource ds) {
        this.ds = ds;
    }
}
编辑:您可以使用apache DBCP作为实例,并以编程方式创建数据源:

BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/TestDB");
ds.setUsername("root");
ds.setPassword("");

现在,您可以插入测试中使用的所需数据源

编辑了我的问题,以便更清楚地说明我想做什么。为您提供了另一个想法。祝你好运编辑了我的问题,让我更清楚一点,我想做什么。我已经编辑了我的代码。你能在帖子里查一下我的编辑II吗?你为什么要实例化InitialContext?您不需要将数据源注入RMI注册表。只需实现一个getter来返回数据源。只需在测试中创建数据源并将其设置在DAO中。先生,您将获得诺贝尔奖!它工作得很好。使用这些函数扩展了我的DBConnection类。然后将数据源放入我的设置中。它工作得很好!非常感谢。