【发布时间】:2015-02-08 20:30:29
【问题描述】:
我遇到了模拟数据访问逻辑的问题。
我正在使用 JavaEE、Struts 和我的自定义数据访问逻辑开发 Web 应用程序。在这个应用程序中,Struts Action 使用 UserDao 来检索用户对象。 UserDao 对象的生命周期与 JDBC 事务相关联。想法是,当 Action 创建 UserDao 对象时,它启动 JDBC 事务(必要的 JDBC 内容存储在 UserDao 对象中),所有 UserDao 方法的调用都在单个 JDBC 事务中运行,然后 Action 终止 UserDao 对象完成事务(提交或回滚)。
问题在于,在操作测试期间,我需要模拟此 UserDao 以使其返回带有必要测试数据的用户对象。
到目前为止,我发现的唯一解决方案是可怕的。我做了以下工作:将 UserDao 拆分为 UserDao 接口和实现它的 UseDaoImpl 类。该接口也将由返回必要的测试数据的 UserDaoMock 实现。接下来,我需要一些在生产运行中返回真实 UserDao (UserDaoImpl) 并在测试运行中模拟 (UserDaoMock) 的结构。此结构不应依赖于 UserDaoMock 类型,以使应用程序的生产代码独立于模拟 .class 文件。这导致了糟糕的设计。
缺点:
在织物中:
-
我需要一个在 Fabric 中的 UserDao 实例,以便能够调用
instantiate方法(实际上是复制构造函数,但我需要它是一种能够使用多态性在 UserDaoImpl 或 UserDaoMock 之间进行选择的方法)创建另一个将与事务关联的 UserDao 对象(取决于运行时类型,它将是 UserDaoImpl 或 UserDaoMock 的instantiate方法)。公共类 UserDaoFabric {
private static UserDaoFabric instance; private UserDao exampleUserDao; public static UserDaoFabric getInstance() { if(instance == null) { instance = new UserDaoFabric(); // By default we initialize fabric with example of real UserDao. // In test will be set to example of UserDaoMock with setExampleUserDao method. instance.setExampleUserDao(new UserDaoImpl(true)); } return instance; } public UserDao getUserDao() { return exampleUserDao.instantiate(true); } public void setExampleUserDao(UserDao userDao) { this.exampleUserDao = userDao; }}
在 UserDao 中:
- 我需要
instantiate方法(实际上是复制构造函数)。 - 尽管我还需要公共构造函数来使用示例初始化结构。
-
构造函数应该有参数告诉它是否是示例(是否开始事务)。
public final class UserDaoImpl implements UserDao { private DataSource ds; private Connection conn; public UserDao instantiate(boolean startTransaction) { if(startTransaction) { return new UserDaoImpl(true); } else { return new UserDaoImpl(false); } } public UserDaoImpl(boolean initiate) { if(initiate) { // DB connection initialization and start of the transaction } else { new UserDaoImpl(); } } private UserDaoImpl() { } @Override public void terminate(boolean commit) { // End of the transaction and DB connection termination } // Other interface methods implementation ommited }
在这种情况下,有没有办法为可测试性进行适当的设计?
或者,如果不是,也许问题的原因是我决定将 UserDao 生命周期与 JDBC 事务联系起来?有哪些可能的替代方案?使 UserDao 单例?如果所有数据库交互都通过单个对象完成,这不会成为应用程序瓶颈吗?
或者,您能否建议另一种更容易模拟的数据访问模式?
请帮忙。这是我一生中做过的最糟糕的设计。
提前致谢。
【问题讨论】:
标签: java unit-testing jdbc mocking dao