使用数据填充ResultSet的简便方法

Dia*_*orD 21 java unit-testing jdbc resultset mocking

我想模拟一个ResultSet.认真.我正在重构一个从ResultSet中解析数据的大型复杂代码,我希望我的代码行为相同.所以,我需要为被重构的部分编写一个单元测试,以便能够对此进行测试.

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

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

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

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

mat*_*att 38

我从这里获得了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());
Run Code Online (Sandbox Code Playgroud)


Yis*_*hai 11

据我所知,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));
}
Run Code Online (Sandbox Code Playgroud)


小智 6

Mockrunner可以加载CSV或XML文件并自动创建MockResultSet.它还可以模拟Connection和Statement,因此所有JDBC的东西都可以工作,甚至不需要在类路径中添加JDBC驱动程序.


kar*_*k m 5

我为同样的案子写过一些东西.您可以使用Mockito模拟结果集.您也可以通过使用这段代码模拟resultset.next()来循环遍历结果集的模拟行.

// two dimensional array mocking the rows of database.
String[][] result = { { "column1", "column2" }, { "column1", "column2" } };

@InjectMocks
@Spy
private TestableClass testableClass;

@Mock
private Connection connection;

@Mock
private Statement statement;

@Mock
private ResultSet resultSet;

@BeforeTest
public void beforeTest() {
    MockitoAnnotations.initMocks(this);
}

@BeforeMethod
public void beforeMethod() throws SQLException {
    doAnswer(new Answer<Connection>() {
        public Connection answer(InvocationOnMock invocation)
                throws Throwable {
            return connection;

        }
    }).when(testableClass).getConnection();

    when(connection.createStatement()).thenReturn(statement);
    when(statement.executeQuery(anyString())).thenReturn(resultSet);
    final AtomicInteger idx = new AtomicInteger(0);
    final MockRow row = new MockRow();

    doAnswer(new Answer<Boolean>() {

        @Override
        public Boolean answer(InvocationOnMock invocation) throws Throwable {
            int index = idx.getAndIncrement();
            if (result.length > index) {
                String[] current = result[index];
                row.setCurrentRowData(current);
                return true;
            } else
                return false;

        }

        ;
    }).when(resultSet).next();

    doAnswer(new Answer<String>() {

        @Override
        public String answer(InvocationOnMock invocation) throws Throwable {
            Object[] args = invocation.getArguments();
            int idx = ((Integer) args[0]).intValue();
            return row.getColumn(idx);
        }

        ;
    }).when(resultSet).getString(anyInt());
}

static class MockRow {
    String[] rowData;

    public void setCurrentRowData(String[] rowData) {
        this.rowData = rowData;
    }

    public String getColumn(int idx) {
        return rowData[idx - 1];
    }
}
Run Code Online (Sandbox Code Playgroud)