我正在处理一个Oracle 10g数据库,并提供了以下存储过程:
procedure get_synopsis (
p_id in my_schema.products.p_id%type,
p_synopses out sys_refcursor); -- cursor of - synopsis_type, synopsis_text
在我的Java代码中,我以这种方式准备语句:
String idForDb = fromIdUrlToIdDb(prodIdUrl);
statement.registerOutParameter(1, OracleTypes.VARCHAR);
statement.setString(1, idForDb );
statement.registerOutParameter(2, OracleTypes.CURSOR);
我通过这种方式获得我需要的数据:
String defaultSyn, nonDefSyn;
String returnedId = ((OracleCallableStatement)stm).getString(1);
try ( ResultSet synopses = ((OracleCallableStatement)stm).getCursor(2) ){ // p_synopses - cursor of: synopsis_type, synopsis_text
while( synopses!=null && synopses.next() ){
String type = synopses.getString(1) != null ? synopses.getString(1).toUpperCase() : null;
if( type != null ){
StringBuilder sb = new StringBuilder();
BufferedReader br = new BufferedReader( synopses.getClob(2).getCharacterStream() );
String line;
while ((line = br.readLine()) != null) {
sb.append(line).append("n");
}
if("DEFAULT".equals(type)){
defaultSyn = sb.toString();
}else if("NONDEFAULT".equals(type)){
nonDefSyn = sb.toString();
}
// ...
}
}
}
在我的测试中,如何模拟(OracleCallableStatement)stm.getCursor(2)
我正在尝试org.jmockery,但没有成功:
Mockery mockery_inner = new Mockery();
final ResultSet mocked_resultset = mockery_inner.mock(ResultSet.class);
mockery_inner.checking(new Expectations() {{
allowing(mocked_resultset).getString(1); will(returnValue("TEST_SYNOPSES-TYPE"));
allowing(mocked_resultset).getClob(2); will(returnValue("TEST_CLOooooooB"));
}});
Mockery mockery = new Mockery();
final CallableStatement statement = mockery.mock(CallableStatement.class);
mockery.checking(new Expectations() {{
allowing(statement).getString(1); will(returnValue("TEST_RETURNED-PROD-ID"));
allowing(statement).getCursor(2); will(returnValue(mocked_resultset)); // cannot find symbol getCursor(int). Location: interface java.sql.CallableStatement
}});
原因很明显是:找不到符号getCursor(int)。位置:接口java.sql.CallableStatement.
如果我尝试允许((OracleCallableStatement)语句).getCursor(2),我会得到"java.lang.ClassCastException.com.sun.proxy.$Proxy6不能强制转换为oracle.jdbc.driver.OracleCallableStatements"。注意:OracleCallableStatement不是一个接口,因此不能用Mockery进行模拟。
我正在尝试使用"手动"mock,但在创建实例时遇到了问题。。
class MockOracleCallableStatement implements OracleCallableStatement {
ResultSet mocked_resultset;
public MockOracleCallableStatement(){
Mockery mockery_inner = new Mockery();
mocked_resultset = mockery_inner.mock(ResultSet.class);
mockery_inner.checking(new Expectations() {{
allowing(mocked_resultset).getString(1); will(returnValue("DEFAULT")); // will pick value from an array
allowing(mocked_resultset).getClob(2); will(returnValue("TEST_CLOooooooooooB"));
}});
}
@Override
ResultSet getCursor(int paramIndex) throws SQLException{
return mocked_resultset;
}
@Override
String getString(int paramIndex) throws SQLException{
return "mockedGetString1--test";
}
}
简而言之,不要。
模拟JDBC(以及许多其他事情)是一件愚蠢的事情,它不会测试你认为它正在测试的东西,但会花费你大量的时间。
您真的应该编写一个集成测试,该测试实际应用于您的数据库。这是验证数据库代码是否正确的唯一方法。如果可以的话,使用与生产中完全相同的数据库版本,如果不使用像H2*这样的内存中数据库的话。
我为JAX杂志写了一篇关于这个主题的文章,这篇文章将更加详细。
- 尽管由于兼容性的原因,这还有其他问题
我似乎可以使用Mockito。。
OracleCallableStatement oraCSMock = Mockito.mock(OracleCallableStatement.class);
更新:方法CLOB.getDBAccess(connection)是CLOB类的静态方法,因此不能用Mockito进行模拟。(您可以使用Powermock模拟静态)。
我最终只测试了404案例:
ResultSet mocked_resultset = Mockito.mock(ResultSet.class);
doReturn( null ) // cant' use "DEFAULT" since getClob() will throw npe anyway. Will test just 404 then.
.when(mocked_resultset).getString(1);
doReturn( false )
.when(mocked_resultset).next(); // or just return null in getCursor(2)
OracleCallableStatement statement = Mockito.mock(OracleCallableStatement.class);
doReturn( "testID" )
.when( statement ).getString(1);
doReturn( mocked_resultset )
.when( statement ).getCursor(2);