Core Java
Java: Mocking a ResultSet using Mockito
This post shows how you can mock a java.sql.ResultSet
using Mockito. It can be used to help unit test code which performs operations on ResultSet
s (such as a ResultSetExtractor
) without relying on an external datasource.
You can create a MockResultSet
by providing a list of column names and a 2D array of data. For example:
var rs = MockResultSet.create( new String[] { "name", "age" }, //columns new Object[][] { // data { "Alice", 20 }, { "Bob", 35 }, { "Charles", 50 } });
The code for MockResultSet
is shown below (also available in my GitHub Repository). Note that I have only mocked a few methods such as next
, getString
and getObject
but it is quite easy to mock the rest by following the same pattern.
public class MockResultSet { private final Map<String, Integer> columnIndices; private final Object[][] data; private int rowIndex; private MockResultSet(final String[] columnNames, final Object[][] data) { // create a map of column name to column index this.columnIndices = IntStream.range(0, columnNames.length) .boxed() .collect(Collectors.toMap( k -> columnNames[k], Function.identity(), (a, b) -> { throw new RuntimeException("Duplicate column " + a); }, LinkedHashMap::new )); this.data = data; this.rowIndex = -1; } private ResultSet buildMock() throws SQLException { final var rs = mock(ResultSet.class); // mock rs.next() doAnswer(invocation -> { rowIndex++; return rowIndex < data.length; }).when(rs).next(); // mock rs.getString(columnName) doAnswer(invocation -> { final var columnName = invocation.getArgumentAt(0, String.class); final var columnIndex = columnIndices.get(columnName); return (String) data[rowIndex][columnIndex]; }).when(rs).getString(anyString()); // mock rs.getObject(columnIndex) doAnswer(invocation -> { final var index = invocation.getArgumentAt(0, Integer.class); return data[rowIndex][index - 1]; }).when(rs).getObject(anyInt()); final var rsmd = mock(ResultSetMetaData.class); // mock rsmd.getColumnCount() doReturn(columnIndices.size()).when(rsmd).getColumnCount(); // mock rs.getMetaData() doReturn(rsmd).when(rs).getMetaData(); return rs; } /** * Creates the mock ResultSet. * * @param columnNames the names of the columns * @param data * @return a mocked ResultSet * @throws SQLException */ public static ResultSet create( final String[] columnNames, final Object[][] data) throws SQLException { return new MockResultSet(columnNames, data).buildMock(); } }
Published on Java Code Geeks with permission by Fahd Shariff, partner at our JCG program. See the original article here: Java: Mocking a ResultSet using Mockito Opinions expressed by Java Code Geeks contributors are their own. |
Hey Thanks a lot!
bhai how to use it in our project
I think that this implementation missed that ResultSet implementations should follow the contract that column indexes start from 1 and not 0, see:
https://docs.oracle.com/en/java/javase/17/docs/api/java.sql/java/sql/ResultSet.html#getObject(int)
More simple implementation! import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.List; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class MockResultSet { private final List<String> columnNames; private final Object[][] data; private int rowIndex; private MockResultSet(final List<String> columnNames, final Object[][] data) { this.columnNames = columnNames; this.data = data; this.rowIndex = -1; } private ResultSet buildMock() throws SQLException { final var resultSet = mock(ResultSet.class); // mock resultSet.next() when(resultSet.next()) .thenAnswer(invocation -> { rowIndex++; return rowIndex < data.length; }); // mock resultSet.getObject(columnName) when(resultSet.getObject(anyString())) .thenAnswer(invocation -> { final var columnName = invocation.getArgument(0, String.class); final var columnIndex = columnNames.indexOf(columnName); return data[rowIndex][columnIndex];… Read more »