模拟连接
适用于 ✅ 开源版 ✅ 专业版 ✅ 企业版
在为您的数据访问层编写单元测试时,您可能已经使用了一些流行的提供程序(如 Mockito、jmock、mockrunner 甚至 DBUnit)提供的通用模拟工具。使用 jOOQ,您可以利用内置的 JDBC 模拟 API,该 API 允许您在 JDBC 级别模拟一个简单的数据库,专门用于 jOOQ 支持的 SQL/JDBC 用例。
免责声明:使用此 jOOQ API 模拟 JDBC 连接的一般想法是使用非常简单的 JDBC 抽象来提供快速的解决方案、注入点等。不建议 使用此模拟 API 来模拟整个数据库(包括复杂的状态转换、事务、锁定等)。一旦您有此需求,请考虑使用实际的数据库产品进行集成测试,而不是在 MockDataProvider
中实现您的测试数据库。
模拟 JDBC API
JDBC 是一个非常复杂的 API。编写一个有用且正确的模拟实现需要花费大量时间,至少需要实现以下接口:
-
java.sql.Connection
-
java.sql.Statement
-
java.sql.PreparedStatement
-
java.sql.CallableStatement
-
java.sql.ResultSet
-
java.sql.ResultSetMetaData
或者,您甚至可能想要实现接口,例如 java.sql.Array
、java.sql.Blob
、java.sql.Clob
以及许多其他接口。除此之外,您可能还需要找到一种方法来同时支持不兼容的 JDBC 次要版本,例如 4.0、4.1。
使用 jOOQ 自己的模拟 API
当使用 jOOQ 自己的模拟 API 时,这项工作会大大简化。org.jooq.tools.jdbc
包包含 JDBC 4.0 和 4.1 的所有基本实现,这些实现是模拟 jOOQ 的 JDBC 所需的。为了编写模拟测试,请为 jOOQ Configuration 提供一个 MockConnection
,并实现 MockDataProvider
// Initialise your data provider (implementation further down): MockDataProvider provider = new MyProvider(); MockConnection connection = new MockConnection(provider); // Pass the mock connection to a jOOQ DSLContext: DSLContext create = DSL.using(connection, SQLDialect.ORACLE); // Execute queries transparently, with the above DSLContext: Result<BookRecord> result = create.selectFrom(BOOK).where(BOOK.ID.eq(5)).fetch();
正如您所看到的,配置设置很简单。现在,MockDataProvider
充当您与 JDBC / jOOQ 的单一联系点。它透明地统一了这些执行模式:
- 没有结果的语句
- 没有结果但具有生成键的语句
- 具有结果的语句
- 具有多个结果的语句
- 具有单查询和多绑定值集的批量语句
- 具有多查询且没有绑定值的批量语句
以上是 jOOQ 支持的执行模式。无论您使用 jOOQ 的哪种获取模式(例如 pojo 获取、延迟获取、多重获取、稍后获取)都无关紧要,因为这些模式都建立在标准 JDBC API 之上。
实现 MockDataProvider
现在,这是如何实现 MockDataProvider
的方法:
public class MyProvider implements MockDataProvider { @Override public MockResult[] execute(MockExecuteContext ctx) throws SQLException { // You might need a DSLContext to create org.jooq.Result and org.jooq.Record objects DSLContext create = DSL.using(SQLDialect.ORACLE); MockResult[] mock = new MockResult[1]; // The execute context contains SQL string(s), bind values, and other meta-data String sql = ctx.sql(); // Exceptions are propagated through the JDBC and jOOQ APIs if (sql.toUpperCase().startsWith("DROP")) { throw new SQLException("Statement not supported: " + sql); } // You decide, whether any given statement returns results, and how many else if (sql.toUpperCase().startsWith("SELECT")) { // Always return one record Result<Record2<Integer, String>> result = create.newResult(AUTHOR.ID, AUTHOR.LAST_NAME); result.add(create .newRecord(AUTHOR.ID, AUTHOR.LAST_NAME) .values(1, "Orwell")); mock[0] = new MockResult(1, result); } // You can detect batch statements easily else if (ctx.batch()) { // [...] } return mock; } }
本质上,MockExecuteContext
包含所有必要的信息,供您决定应返回哪种数据。MockResult
封装了两个信息:
您应该返回与查询执行次数(在 批量模式 下)或结果数(在 多重获取模式 下)一样多的 MockResult
对象。但是,您可以构造一个“更友好”的 org.jooq.Result
,其中包含您自己的记录类型,而不是笨拙的 java.sql.ResultSet
。jOOQ 模拟 API 将使用此 Result
提供的元数据来创建必要的 JDBC java.sql.ResultSetMetaData
。
有关您应遵循的规则列表,请参阅 MockDataProvider Javadoc
。
反馈
您对此页面有任何反馈吗?我们很乐意听取您的意见!