用数据填充结果集的简单方法

2022-09-01 19:54:16

我想模拟一个结果集。认真地。我正在重构一段非常复杂的代码,它正在解析来自ResultSet的数据,我希望我的代码行为相同。因此,我需要为正在重构的片段编写一个单元测试,以便能够对此进行测试。

在谷歌搜索后,我想出了2个想法:

  1. 使用EasyMock,写looooong嘲笑序列。非常糟糕的解决方案:难以添加初始数据,难以更改数据,大测试调试过程。
  2. 使用Apache Derby或HSQLDB创建内存数据库,从文件或字符串数组中填充它,使用一些神奇的InMemoryDBUtils.query(sql)进行查询。然后使用该结果集。不幸的是,我没有找到任何神奇的InMemoryDBUtils来快速编写测试:-)。IBM文章“使用Derby进行持久性的隔离单元测试”似乎对我的需求很好,但是......

第二种方法看起来更容易,也更容易支持。

对于创建这样的模拟,你有什么建议?(尽管有医生,当然:-)?我是否错过了一颗眉毛,一些银弹?可能,DBUnit是这个工具吗?


答案 1

我从这里开始成功使用MockResultSet类:http://mockrunner.sourceforge.net/。它允许您创建一个实现 ResultSet 接口的类,并允许您设置每列和每行的值。

如果您的方法使用合理大小的 ResultSet,则应该能够创建返回所需值的测试。

下面是一个简单的示例:

MockResultSet rs = new MockResultSet("myMock");

rs.addColumn("columnA", new Integer[]{1});
rs.addColumn("columnB", new String[]{"Column B Value"});
rs.addColumn("columnC", new Double[]{2});

// make sure to move the cursor to the first row
try
{
  rs.next();
}
catch (SQLException sqle)
{
  fail("unable to move resultSet");
}

// process the result set
MyObject obj = processor.processResultSet(rs);

// run your tests using the ResultSet like you normally would
assertEquals(1, obj.getColumnAValue());
assertEquals("Column B Value", obj.getColumnBValue());
assertEquals(2.0d, obj.getColumnCValue());

答案 2

据我所知,DBUnit不会显示结果集,尽管它可以帮助您填充内存数据库。

我想说的是,在这一点上,嘲弄框架是错误的方法。嘲笑是关于测试行为和交互,而不仅仅是返回数据,所以它可能会妨碍你。

相反,我要么实现结果集接口,要么创建结果集接口的动态代理到一个类,该类实现您关心的方法而无需实现整个结果集。您可能会发现维护类与维护内存数据库一样简单(前提是所测试的数据集是一致的),并且可能更容易调试。

您可以使用 DBUnit 备份该类,在 DBUnit 中,您可以使用 dbunit 拍摄结果集的快照,并让 dbunit 在测试期间从 xml 读回它,并让虚拟结果集从 dbunit 的类中读取数据。如果数据稍微复杂,这将是一个合理的方法。

如果这些类是如此耦合,以至于它们需要读取作为同一测试的一部分修改的数据,我会选择内存数据库。即便如此,我还是会考虑使用真实数据库的副本,直到您设法将该依赖项分开为止。

一个简单的代理生成方法:

private static class SimpleInvocationHandler implements InvocationHandler {
    private Object invokee;

    public SimpleInvocationHandler(Object invokee) {
        this.invokee = invokee;
    }

    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        method = invokee.getClass().getMethod(method.getName(), method.getParameterTypes());
        if (!method.isAccessible()) {
            method.setAccessible(true);
        }
        try {
            return method.invoke(invokee, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        }
    }
}

public static <T> T generateProxy(Object realObject, Class... interfaces) {
    return (T) Proxy.newProxyInstance(realObject.getClass().getClassLoader(), interfaces, new SimpleInvocationHandler(realObject));
}

推荐