是否有一组JDBC存根/模拟可用于任何地方?

时间:2009-07-20 02:51:00

标签: java unit-testing jdbc tdd

在过去的几年里,我一直在努力研究单元测试数据库代码以及随之而来的所有痛苦。我发现这个现有的帖子我觉得非常有启发性:

接受回答的作者表示,为了验证生成的SQL,模拟整个数据库层可能很有用。几个月前,当我第一次阅读答案时,我没有想太多,但最近我发现了SQL生成错误,错误分配字段等导致的一些错误。我确实意识到JDBC相当臃肿且容易出错,但此时不能选择切换到不同的东西。

有问题的应用程序是数据源的批处理器,直接使用JDBC而不是ORM。除了实际的实现之外,所有JDBC代码都被分成不同的DAO对象,其中每个对象都有自己的接口和存根。这使我能够实现业务层的良好测试覆盖率,但数据库层的测试几乎不存在。

是否存在JDBC(java.sql)接口的现有存根实现,可以将其注入DAO类并用于验证生成的SQL并可能发回一些预编程的结果?

6 个答案:

答案 0 :(得分:7)

我不知道你是否看过它,但有MockRunner。它提供了许多实现JDBC接口(以及其他J2EE类)的类。这是the JDBC mock objects。还有不少examples

答案 1 :(得分:4)

听起来你在DAO代码中遇到了问题?否则,DAO层是进行模拟的明显位置,但是如果你试图测试DAO,那么你需要模拟下面的那些。

就个人而言,我倾向于远离嘲弄大型复杂的图书馆;如果您确实需要直接测试DAO层并且DAO直接使用JDBC,那么您有三个明显的选择:

  1. 运行包含DAO和JDBC以及数据库的集成测试
  2. 使用更薄的界面在JDBC上方添加一个图层,更适合模拟。
  3. 使用您自己编写的JDBC模拟或上面列出的某些项目。
  4. 我几乎总是选择#1或#2。因为在格式错误的SQL语法等方面存在大量错误的可能性,所以我倾向于倾向于#1。然而,我意识到这不是你所要求的。 ;)

答案 2 :(得分:2)

您可以使用dbunit直接测试数据库。

答案 3 :(得分:2)

jOOQ附带MockConnection,可以提供MockDataProvider,这比完整的JDBC API更容易实现。这篇博客文章展示了如何使用MockConnection: http://blog.jooq.org/2013/02/20/easy-mocking-of-your-database/

一个例子:

MockDataProvider provider = new MockDataProvider() {

    // Your contract is to return execution results, given a context
    // object, which contains SQL statement(s), bind values, and some
    // other context values
    @Override
    public MockResult[] execute(MockExecuteContext context) 
    throws SQLException {

        // Use ordinary jOOQ API to create an org.jooq.Result object.
        // You can also use ordinary jOOQ API to load CSV files or
        // other formats, here!
        DSLContext create = DSL.using(...);
        Result<MyTableRecord> result = create.newResult(MY_TABLE);
        result.add(create.newRecord(MY_TABLE));

        // Now, return 1-many results, depending on whether this is
        // a batch/multi-result context
        return new MockResult[] {
            new MockResult(1, result)
        };
    }
};

// Put your provider into a MockConnection and use that connection
// in your application. In this case, with a jOOQ DSLContext:
Connection connection = new MockConnection(provider);
DSLContext create = DSL.using(connection, dialect);

// Done! just use regular jOOQ API. It will return the values
// that you've specified in your MockDataProvider
assertEquals(1, create.selectOne().fetch().size());

还有MockFileDatabase,它可以帮助您通过编写如下文本文件来匹配虚拟结果和SQL字符串:

# This is a sample test database for MockFileDatabase
# Its syntax is inspired from H2's test script files

# When this query is executed...
select 'A' from dual;
# ... then, return the following result
> A
> -
> A
@ rows: 1

# Just list all possible query / result combinations
select 'A', 'B' from dual;
> A B
> - -
> A B
@ rows: 1

select "TABLE1"."ID1", "TABLE1"."NAME1" from "TABLE1";
> ID1 NAME1
> --- -----
> 1   X
> 2   Y
@ rows: 2

答案 4 :(得分:1)

虽然我一般都非常喜欢单元测试,但我发现DAO的价值有限。

我所看到的是完全有可能编写测试(使用任何模拟API - JMockEasyMock等),它们通常是直接工作的(逻辑)如此基本,他们怎么可能不会)只在你更改代码时断开(例如添加一个值),这只会使它们成为代码库的负担。

我认为这是因为我的DAO通常遵循以下形式:

  • 获得连接。
  • 创建声明。
  • 设定值。
  • 读取值(用于加载操作)。
  • 清理。
然后,您可以假设JDBC驱动程序将如何工作(并且),并且您获得的测试实际上只是测试一些简单的代码按照声明的顺序调用。

源自DAO的错误通常发生在数据库中(密钥违规,存储过程中的错误等),除非您作为一个整体运行系统,否则您不会看到这些错误。

这些天我倾向于让更高级别的测试 - 集成等 - 运用DAO代码来实现这样做,并希望能够及早发现我提到的那种错误。

答案 5 :(得分:0)

如果你想测试持久层(ORM,DAO,...)根据各种JDBC情况按预期运行(例如,当它获得这样的结果集/更新计数时,那么它应该这样做那样),然后是Acolyte必须考虑框架。

它允许构建您通过处理程序管理的JDBC连接,因此您可以选择为每个查询/更新返回的内容:https://github.com/cchantep/acolyte

披露:这是我的框架。