确定性地使DB失败以进行测试

时间:2012-10-22 07:02:51

标签: java testing

我有一个Java应用程序,它对数据库使用了大量java.sql.Connection

我想测试一下,如果数据库不可用,我的服务会返回相应的错误代码(区分临时和永久性问题,例如HTTP 500和503)。

为了进行测试,我的应用程序连接到嵌入式本地内存中的h2数据库;应用程序没有意识到这一点,只有我的集成测试是。

如何确定性地对数据库进行写入失败,例如挂钩提交并让他们抛出自定义SQLException?我希望测试代码中的全局“数据库不可用”布尔值影响所有连接,并使我的应用程序运行其重新连接逻辑。

(我已经开始代理Connection并在if(failFlag) throw new MySimulateFailureException()中添加commit();但这并没有抓住PreparedStatement.executeUpdate();在我开始代理之前PreparedStatement也是 - 它有很多方法! - 我希望以更好的方式教授......)

2 个答案:

答案 0 :(得分:1)

我认为这是使用aspects的合适人选。用例如。 Spring切入整个软件包或只是某些你希望失败的方法是非常容易的 - 特别是你可以before建议总是抛出ConnectException或者做一些更高级的around {1}}建议。

干杯,

答案 1 :(得分:0)

我最终制作了自己的Java反射包装器,它拦截了Connection.commitPreparedStatement.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));
}