模拟连接
适用于 ✅ 开源版 ✅ 专业版 ✅ 企业版
在为您的数据访问层编写单元测试时,您可能已经使用了一些流行的提供程序(如 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。
反馈
您对此页面有任何反馈吗?我们很乐意听取您的意见!