我有一个Java应用程序,它对数据库使用了大量java.sql.Connection
。
我想测试一下,如果数据库不可用,我的服务会返回相应的错误代码(区分临时和永久性问题,例如HTTP 500和503)。
为了进行测试,我的应用程序连接到嵌入式本地内存中的h2数据库;应用程序没有意识到这一点,只有我的集成测试是。
如何确定性地对数据库进行写入失败,例如挂钩提交并让他们抛出自定义SQLException
?我希望测试代码中的全局“数据库不可用”布尔值影响所有连接,并使我的应用程序运行其重新连接逻辑。
(我已经开始代理Connection
并在if(failFlag) throw new MySimulateFailureException()
中添加commit()
;但这并没有抓住PreparedStatement.executeUpdate()
;在我开始代理之前PreparedStatement
也是 - 它有很多方法! - 我希望以更好的方式教授......)
答案 0 :(得分:1)
我认为这是使用aspects的合适人选。用例如。 Spring切入整个软件包或只是某些你希望失败的方法是非常容易的 - 特别是你可以before
建议总是抛出ConnectException
或者做一些更高级的around
{1}}建议。
干杯,
答案 1 :(得分:0)
我最终制作了自己的Java反射包装器,它拦截了Connection.commit
和PreparedStatement.execute...
方法。
我在'DBFactory'课程中的最终代码:
@SuppressWarnings("serial")
public class MockFailureException extends SQLException {
private MockFailureException() {
super("The database has been deliberately faulted as part of a test-case");
}
}
private class MockFailureWrapper implements InvocationHandler {
final Object obj;
private MockFailureWrapper(Object obj) {
this.obj = obj;
}
@Override public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
if(dbFailure && ("commit".equals(m.getName()) || m.getName().startsWith("execute")))
throw new MockFailureException();
Object result;
try {
result = m.invoke(obj, args);
if(result instanceof PreparedStatement)
result = java.lang.reflect.Proxy.newProxyInstance(
result.getClass().getClassLoader(),
result.getClass().getInterfaces(),
new MockFailureWrapper(result));
} catch (InvocationTargetException e) {
throw e.getTargetException();
} catch (Exception e) {
throw new RuntimeException("unexpected invocation exception: " + e.getMessage());
}
return result;
}
}
public Connection newConnection() throws SQLException {
Connection connection = DriverManager.getConnection("jdbc:h2:mem:"+uuid+";CREATE=TRUE;DB_CLOSE_ON_EXIT=FALSE");
return (Connection)java.lang.reflect.Proxy.newProxyInstance(
connection.getClass().getClassLoader(),
connection.getClass().getInterfaces(),
new MockFailureWrapper(connection));
}