刺激 JDBC

Spocking the JDBC

我有一些 Groovy 2.4.x 代码使用了一些 JDBC:

class WidgetPersistor {
    @Inject // Gets injected correctly by Guice, don't worry about it!
    DataSource dataSource

    Fizz getFizzByWidgetName(String name) {
        Connection conn
        PreparedStatement ps
        ResultSet rs

        try {
          // JDBC code here
        } catch(SQLException sqlExc) {
            if(conn) {
                try {
                    // NOTE: At the end of the day, I just want to verify
                    // that, given the 'name' arg to this method, the rollback
                    // doesn't fire!
                    conn.rollback()
                } catch(SQLException rollBackExc) {
                    throw rollBackExc
                }
            }
            throw sqlExc
        } finally {
            if(conn) {
                try {
                    rs.close()
                    ps.close()
                    conn.close()
                } catch(SQLException closingExc) {
                    throw closingExc
                }
            }
        }   
    }
}

我正在尝试编写一个 Spock 测试来执行此 getFizzByWidgetName 方法并验证 conn.rollback() 方法 never 执行(意味着我们从未尝试过回滚)。

这是我的最佳尝试:

def "getFizzByWidgetName succeeds without rollback"() {
  given: "data client with db connections"
    // Don't worry about how I get this for my test, but its a legit JDBC connection
    DataSource ds = provideDataSource()

    Connection mockConn = Mock(Connection)
    PreparedStatement mockPS = Mock(PreparedStatement)
    ResultSet mockRS = Mock(ResultSet)

    mockPS.executeQuery() >> mockRS
    mockConn.prepareStatement(_) >> mockPS
    ds.connection >> mockConn   // ??? Its like I want the DataSource half-mocked...

    WidgetPersistor client = new WidgetPersistor(mockDS)

    when: "we try to query something"
    client.getFizzByWidgetName('fizzbuzz')

    then: "we dont get any errors"
    0 * mockConn.rollback()
}

知道我哪里出错了吗?

如果您使用的是来自真实数据库的数据源,并且您的测试代码是用 Groovy 编写的(看起来是这样),您可以使用元类来测试此类事情:

DataSource ds = provideDataSource()

def connection = ds.connection
connection.metaClass.rollback = { throw new AssertionError("rollback called") }
ds.metaClass.connection = connection  

但它不是很漂亮。您可能应该在不使用模拟的情况下调用您的方法,并测试数据库的状态(即数据已提交,未回滚)