如何在 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 7
、JSP
、Servlets
、Log4j
和 mysql
进行我的项目。
我用谷歌搜索这个问题好几个小时都没有正确答案。
如何使用DataSource
和JUnit
测试我的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?的主要内容,如果未能解决你的问题,请参考以下文章