如何在 Java Web 应用程序中使用 DataSource 测试 DAO?

Posted

技术标签:

【中文标题】如何在 Java Web 应用程序中使用 DataSource 测试 DAO?【英文标题】:How to test DAO with DataSource in Java Web Application? 【发布时间】:2015-04-07 12:25:27 【问题描述】:

我正在使用Tomcat 7JSPServletsLog4jmysql 进行我的项目。

我用谷歌搜索这个问题好几个小时都没有正确答案。

如何使用DataSourceJUnit 测试我的DAO?

我最近发现了这个article,但不知道如何为我的目的配置它,也不确定这是否是一个好方法。

我有以下 DAO 层次结构:

我通过以下方式在AbstractRepository 中获取DataSource

...
protected final DataSource ds;
...
    public AbstractRepository() 
        DataSource dataSource = null;
        try 
            Context initContext = new InitialContext();
            dataSource = (DataSource) initContext
                    .lookup("java:/comp/env/jdbc/mydb");
         catch (NamingException ex) 
            LOG.error("Cannot obtain a connection from the pool", ex);
        
        ds = dataSource;
    
...

您能否为Entrant Repository Test 提供一些解决此问题的方法的代码示例?

【问题讨论】:

【参考方案1】:

AbstractRepository 中的代码很难测试。原因是“智能构造函数”,它知道他从哪里获取数据源。但是你仍然有很多选择。您可以更改代码(我的选择),以便构造函数接收 DataSource 作为参数,例如

public AbstractRepository(DataSource dataSource)
  this.ds = dataSource;

这样您就可以在测试环境中使用一些测试数据源(模拟?)来初始化您的存储库。

或者您可以使用一些模拟框架,例如EasyMock,创建要测试的存储库的部分模拟。 EasyMock 默认情况下创建类的部分模拟而不调用任何构造函数。然后你可以模拟getDataSource() 方法(我希望你有一个,它在你需要访问数据源的任何地方都使用?)返回测试数据源,或者你可以使用反射设置最终的ds 字段(更糟糕的是选择,我想)。

【讨论】:

我做空构造函数的原因是孩子可以根据需要覆盖数据源,但如果孩子使用相同的数据库,它可以使用父数据源。虽然我同意它很难测试。我现在的目标是使用 JUnit 对其进行测试(不使用其他测试框架) 如果我实现了你提出的构造函数,是否意味着每个孩子也需要使用 DataSource 参数创建这样的构造函数?也许通过 Connection 更好? 如果唯一声明的构造函数是AbstractRepository(DataSource)构造函数,那么每个子类都必须显式调用super(DataSource)。在其构造函数中。我建议在每个 MyRepository 扩展 AbstractDatasource 中提供 MyRepository(DataSource) 构造函数。您的存储库不需要知道 DataSource 的来源。 Connection 作为参数传递给AbstractRepository 构造函数是个坏主意。每次事务后都应该关闭一个连接(如果DataSource 是一个连接池,则可以重用该连接)。这意味着您必须创建 Repository 类的新实例,每次您想从数据库中写入或读取实体时,都会将连接传递给 AbstractRepository 构造函数。 我已经创建了一个 DataSource Factory。并且还在每个存储库中添加了带有 DataSource 参数的 costructors。但是有那么一刻,我的 DAO 每次我在 Controller 中需要它时都会创建。我应该重新设计吗?那么哪种设计更可取?【参考方案2】:

问题是您的代码直接从源代码获取其依赖关系。我为您之前的@​​987654321@ 提供的建议类似于 Alexey Gromov 的建议,但如果由于某种原因您无法更改代码,那么您需要在单元测试中执行以下操作:

1) 创建通常使用 Java 容器(例如 Tomcat、Jetty)为您创建的子上下文。这可以使用我从您的链接中获得的以下命令来完成(我已验证它们有效):

    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");

2) 您需要将您的模拟对象绑定到您的代码所期望的名称。这可以使用 InitialContext#bind 来完成。

ic.bind("java:/comp/env/jdbc/DSTest", mock(DataSource.class)); 

请注意,mock 函数是由 Mockito 提供的……你可以传递你想要的任何模拟对象。

在我的实际代码中,我有这个:

        mysqlDs = (DataSource) initCtx.lookup("java:/comp/env/jdbc/DSTest");
        System.out.println("----mysqlDs = " + mysqlDs + "\n");

打印出来的:

----mysqlDs = Mock for DataSource, hashCode: 1926808117

【讨论】:

感谢您的回答!我可以重新设计我的代码。我已经创建了另一个构造函数,并在每个存储库中传递给它的 DataSource 参数。因此,在我的测试中,我认为我可以创建 MysqlDataSource 实例并将其传递给经过测试的存储库,但是在尝试运行此测试时我得到了NPE 你从哪里获得 NPE 以及如何创建模拟 MysqlDataSource? MysqlDataSource ds = new MysqlDataSource(); ds.setServerName("localhost"); ds.setPort(3306); ds.setDatabaseName("mydb"); ds.setUser("root"); ds.setPassword("password"); userRepository = new UserRepository(ds); 我正在使用 JUnit 对其进行测试。这就是我在BeforeClass 中创建数据源的方式。 NPE 引入了创建、更新、删除、查找方法。所以我认为我没有正确获得 MysqlDataSource 使用连接时是NPE吗?就我个人而言,我不会使用真正的 MysqlDataSource,而是使用模拟的 DataSource 和模拟的 Connection。 从我的谷歌搜索如何测试控制器和 dao 我看到大多数人建议使用 Mockito 库。但是有一段时间我没时间了,由于不知道这个框架,我不确定我是否应该现在开始。从上面评论中的代码示例中,我看不出为什么我应该得到任何异常。【参考方案3】:

使用TomcatJNDI,您可以轻松地从您的类中访问Tomcat JNDI 环境,就像您的应用程序在Tomcat 中运行一样。 TomcatJNDI 利用 Tomcat 的 JNDI 系统并通过处理原始 Tomcat 配置文件对其进行配置。它的用法很简单,例如。 g.

TomcatJNDI tomcatJNDI = new TomcatJNDI();
tomcatJNDI.processContextXml(contextXmlFile);
tomcatJNDI.processWebXml(webXmlFile);
tomcatJNDI.start();

之后,您可以像以前一样查找 DataSource。 More information can be found here.

【讨论】:

以上是关于如何在 Java Web 应用程序中使用 DataSource 测试 DAO?的主要内容,如果未能解决你的问题,请参考以下文章

如何从数据库中获取到数据生成饼状图?(Java web)

如何使用 XML 配置 Spring Data JPA

java web 怎么在jtable中添加按钮?

如何在Web页上实现文件上传

java web中怎么阻止页面跳转

在Java中,如何在Web应用程序中动态重新加载资源包?