使数据库确定性地失败以进行测试 [英] Make DB fail deterministically for testing

查看:21
本文介绍了使数据库确定性地失败以进行测试的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a Java application that uses lots of java.sql.Connection to a database.

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

I want to test that, if the database is unavailable, my services return the appropriate error codes (distinguishing between temporary and permanent problems e.g. HTTP 500 and 503).

为了测试,我的应用程序连接到嵌入式、本地、内存中的 h2 数据库;应用程序不知道这一点,只有我的集成测试是.

For testing, my application connects to an embedded, local, in-memory h2 database; the application is not aware of this, only my integration test is.

我怎样才能确定性地使写入数据库失败,例如挂钩提交并让它们抛出自定义 SQLException?我想要测试代码中的全局数据库不可用"布尔值影响所有连接并使我的应用程序执行其重新连接逻辑.

How can I make writes to the database fail deterministically, e.g. hook into commits and make them throw a custom SQLException? I want a global 'database is unavailable' boolean in the test code that affects all connections and makes my application exercise its reconnect logic.

(我已经开始代理 Connection 并在 commit() 中放置一个 if(failFlag) throw new MySimulateFailureException(); 但这并没有抓住 PreparedStatement.executeUpdate(); 在我开始代理 PreparedStatement 之前 - 它有很多方法! - 我想被教导更好的方法...)

(I had started by proxying Connection and putting an if(failFlag) throw new MySimulateFailureException() in commit(); but this didn't catch PreparedStatement.executeUpdate(); before I embark on proxying the PreparedStatement too - its a lot of methods! - I'd like to be taught a better way...)

推荐答案

我最终制作了自己的 Java 反射包装器来拦截 Connection.commitPreparedStatement.execute... 方法.

I ended up making my own Java reflection wrapper that intercepts Connection.commit and the PreparedStatement.execute... methods.

我在DBFactory"类中的最终代码:

My final code in my 'DBFactory' class:

@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));
}

这篇关于使数据库确定性地失败以进行测试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆