用数据填充 ResultSet 的简单方法

2022-01-08 00:00:00 mocking unit-testing resultset jdbc java

我想模拟一个 ResultSet.严重地.我正在重构一大段复杂的代码,它正在解析来自 ResultSet 的数据,并且我希望我的代码具有相同的行为.所以,我需要为正在重构的部分编写一个单元测试,以便能够对此进行测试.

I want to mock a ResultSet. Seriously. I'm refactoring one big complicated piece of code which is parsing data from ResultSet, and I want my code to behave identically. So, I need to write a unit test for the piece being refactored to be able to test this.

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

After googling I came up with 2 ideas:

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

第二种方法看起来更简单,更容易支持.

Second approach looks somewhat easier and much more supportable.

对于创建这样的模拟,您有什么建议?(尽管有医生,当然:-)?我错过了眉毛一些灵丹妙药吗?可能,DBUnit 就是这个工具?

What would you advice for creating such a mock? (despite doctors, of course :-)? Am I missing an eyebrow some silver bullet? Possibly, DBUnit is the tool for this?

推荐答案

据我所知,DBUnit 不提供结果集,尽管它可以很好地帮助您填充内存数据库.

DBUnit doesn't present a result set, to my knowledge, although it will well help you populate your in memory database.

我会说在这一点上模拟框架是错误的方法.模拟是关于测试行为和交互,而不仅仅是返回数据,因此它可能会妨碍您.

I would say that a mocking framework is the wrong approach at this point. Mocking is about testing behavior and interaction, not just returning data, so it will likely get in your way.

我会改为实现一个结果集接口,或者创建一个结果集接口的动态代理到一个实现您关心的方法的类,而不必实现整个结果集.您可能会发现维护一个类就像维护一个内存数据库一样容易(前提是被测数据集是一致的),而且可能更容易调试.

I would instead either implement a result set interface, or create a dynamic proxy of a result set interface to a class that implements the methods you care about without having to implement the whole result set. You will likely find maintaining a class as easy as maintaining an in memory database (provided that the dataset under test is consistent), and probably easier to debug.

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

You could back up that class with DBUnit, where you take a snapshot of your result set with dbunit, and have dbunit read it back during the test from xml, and have your dummy result set read the data from dbunit's classes. This would be a reasonable approach if the data was mildly complex.

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

I would go for the in memory database if the classes were so coupled that they need to read data that was modified as part of the same test. Even then, I would consider using a copy of the real database until you managed to pull that dependency apart.

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

A simple proxy generation method:

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

相关文章