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:
Second approach looks somewhat easier and much more supportable.
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?
The JDBC ResultSet doesn't provide any isEmpty(), length() or size() method to check if its empty or not. Hence, when a Java programmer needs to determine if ResultSet is empty or not, it just calls the next() method and if next() returns false it means ResultSet is empty.
To create a ResultSet object, you use either the BuildResultSet method or the BuildSQLQuery method of the Session object. Both of these methods return a ResultSet object that is ready to run the query but which contains no data.
executeQuery method to obtain the result table from the SELECT statement in a ResultSet object. In a loop, position the cursor using the next method, and retrieve data from each column of the current row of the ResultSet object using getXXX methods. XXX represents a data type. Invoke the ResultSet.
I've had success with the MockResultSet class from here: http://mockrunner.sourceforge.net/. It allows you to create a class that implements the ResultSet interface, and lets you set the values for each column and row.
If your methods are working with ResultSets of reasonable size, you should be able to create tests that return the values you need fairly easily.
Here's a simple example:
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());
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.
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)); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With