我想为连接到数据库的一些代码编写一些单元测试,运行一个或多个查询,然后处理结果。(没有实际使用数据库)
这里的另一位开发人员编写了我们自己的 DataSource、Connection、Statement、PreparedStatement 和 ResultSet 实现,它们将根据 xml 配置文件返回相应的对象。(我们可以使用伪造的数据源并针对它返回的结果集运行测试)。
我们在这里重新发明轮子吗?单元测试是否已经存在类似的东西?还有其他/更好的方法来测试 jdbc 代码吗?
我想为连接到数据库的一些代码编写一些单元测试,运行一个或多个查询,然后处理结果。(没有实际使用数据库)
这里的另一位开发人员编写了我们自己的 DataSource、Connection、Statement、PreparedStatement 和 ResultSet 实现,它们将根据 xml 配置文件返回相应的对象。(我们可以使用伪造的数据源并针对它返回的结果集运行测试)。
我们在这里重新发明轮子吗?单元测试是否已经存在类似的东西?还有其他/更好的方法来测试 jdbc 代码吗?
你有几个选择:
有时使这些测试可配置是必要且有用的,以便仅在数据库可用时执行这些测试。这可以通过例如构建属性来完成。
我喜欢使用以下组合:
仅使用 DBUnit 和 HSQLDB 就可以走得更远。Unitils 提供了最后一英里的代码来管理和重置数据库状态。它还提供了一种管理数据库模式更改的好方法,并且可以轻松使用特定的 RBDMS(Oracle、DB2、SQL Server 等)。最后,Unitils 围绕 DBUnit 提供了一些很好的包装器,它使 API 现代化并使 DBUnit 更易于使用。
如果您还没有检查过 Unitils,那么您绝对应该检查过。Unitils 经常被忽视和低估。
我会说 HSQL 是您的单元测试期间要走的路。您的测试重点是测试您的 jdbc 代码并确保其正常工作。添加自定义类或模拟 jdbc 调用可以轻松隐藏错误。
我主要使用 mysql,当测试运行驱动程序类时,url 更改为 org.hsqldb.jdbcDriver 和 jdbc:hsqldb:mem:test。
我更喜欢使用EasyMock来测试不太容易测试的代码。
如果您想做单元测试,而不是集成测试,那么您可以使用非常基本且简单的方法,仅使用 Mockito,如下所示:
public class JDBCLowLevelTest {
private TestedClass tested;
private Connection connection;
private static Driver driver;
@BeforeClass
public static void setUpClass() throws Exception {
// (Optional) Print DriverManager logs to system out
DriverManager.setLogWriter(new PrintWriter((System.out)));
// (Optional) Sometimes you need to get rid of a driver (e.g JDBC-ODBC Bridge)
Driver configuredDriver = DriverManager.getDriver("jdbc:odbc:url");
System.out.println("De-registering the configured driver: " + configuredDriver);
DriverManager.deregisterDriver(configuredDriver);
// Register the mocked driver
driver = mock(Driver.class);
System.out.println("Registering the mock driver: " + driver);
DriverManager.registerDriver(driver);
}
@AfterClass
public static void tearDown() throws Exception {
// Let's cleanup the global state
System.out.println("De-registering the mock driver: " + driver);
DriverManager.deregisterDriver(driver);
}
@Before
public void setUp() throws Exception {
// given
tested = new TestedClass();
connection = mock(Connection.class);
given(driver.acceptsURL(anyString())).willReturn(true);
given(driver.connect(anyString(), Matchers.<Properties>any()))
.willReturn(connection);
given(connection.prepareCall(anyString())).willReturn(statement);
}
}
你可以测试各种场景,就像在任何其他 Mockito 测试中一样
@Test
public void shouldHandleDoubleException() throws Exception {
// given
SomeData someData = new SomeData();
given(connection.prepareCall(anyString()))
.willThrow(new SQLException("Prepare call"));
willThrow(new SQLException("Close exception")).given(connection).close();
// when
SomeResponse response = testClass.someMethod(someData);
// then
assertThat(response, is(SOME_ERROR));
}
有DBUnit。它不允许您在没有数据库的情况下测试 jdbc 代码,但您似乎可以通过模拟数据库来引入一组不同的购买。
虽然在您的应用程序中模拟 jdbc 的方式当然取决于您如何实现实际的 jdbc 事务。
如果您按原样使用 jdbc,我假设您已经为自己编写了一个实用程序类来执行DBUtils.getMetadataFor(String tablename)
. 这意味着您必须创建该类的模拟,而这可能就是您所需要的。这对您来说是一个相当简单的解决方案,因为您显然已经有一系列可用的 jdbc 相关模拟对象。请注意,我假设您的 jdbc 代码没有在应用程序周围爆炸——如果是,请重构!!!
但是,如果您使用任何数据库处理框架(例如 Spring Framework 的 JDBC 模板类),您可以并且应该使用 EasyMock 或其他等效物来模拟接口类。这样,您就可以拥有轻松模拟连接所需的所有功能。
最后,如果没有其他方法,您可以执行其他人已经说过的操作并使用 DBUnit 和/或 derby。
我们使用 Mockrunner。http://mockrunner.sourceforge.net/ 它内置了模拟连接和数据源,因此无需您自己实现它们。
Acolyte 驱动程序可用于模拟 JDBC 连接,在测试期间对其进行管理并将数据作为结果集返回(使用其类型安全的行列表 API):https ://github.com/cchantep/acolyte
注意:我是 Acolyte 的作者。