【问题标题】:how to mock resultset and populate it using Mockito in Java如何在 Java 中使用 Mockito 模拟结果集并填充它
【发布时间】:2011-09-10 04:14:52
【问题描述】:

我有用CallableStatement.executeQuery() 填充Resultset 的代码。我嘲笑了ResultSetCallableStatement,但为了测试我必须填充ResultSet 的方法。

这是我正在测试的方法的代码

ResultSet rset = cs.executeQuery();
while (rset.next()) {
IndexVolatilityImpl tsImpl = new IndexVolatilityImpl();
tsImpl.setTradeDate(rset.getString("trade_date"));
tsImpl.setTradeTime(rset.getString("trade_time"));
tsImpl.setExprDate(rset.getString("expr_date"));
tsImpl.setSymbol(rset.getString("symbol"));
tsImpl.setTradePrice(rset.getDouble("trade_price"));
tsImpl.setContractMonth(rset.getString("contract_month"));
tsImpl.setMilliSecs(rset.getString("trade_time_thou"));
colIndexVolatilityImpl.add(tsImpl);

我现在已经模拟了 CallableStatement 和 ResultSet,因为它们被模拟了,所以我的 rset 是空的。我想填充结果集并按照以下方式进行操作

resultSetMock = Mockito.mock(ResultSet.class);
Mockito.when(resultSetMock.getString("trade_date")).thenReturn("03/10/2011");
Mockito.when(resultSetMock.getString("trade_time")).thenReturn("12:24:56");
Mockito.when(resultSetMock.getString("expr_date")).thenReturn("03/19/2011");
Mockito.when(resultSetMock.getString("symbol")).thenReturn("VIX1");
Mockito.when(resultSetMock.getDouble("trade_price")).thenReturn(Double.valueOf("20.96"));
Mockito.when(resultSetMock.getString("contract_month")).thenReturn("1");
Mockito.when(resultSetMock.getString("trade_time_thou")).thenReturn("165");

Mockito.doReturn(resultSetMock).when(callableStatementMock).executeQuery();

但是rsetnull

【问题讨论】:

    标签: java junit4 mockito


    【解决方案1】:

    您还应该模拟 next() 方法使其在第一次调用时返回 true,因为默认情况下 mockito 将返回 false

    Mockito.when(resultSetMock.next()).thenReturn(true).thenReturn(false);
    

    【讨论】:

    • 我正在这样做,但我想填充 ResultSet 并且我正在按照上述方法进行操作,但结果为空。你能解释一下我做错了什么吗?
    • 刚刚尝试了类似的测试代码,它可以工作。所以你的 rset 是 null 吗?我猜cs对象当然是callableStatementMock,在这种情况下它应该可以工作。我以为你的问题只是你的 RS 是空的,但我不明白它怎么可能是空的。
    【解决方案2】:

    我为同样的情况写了一些东西。您可以使用 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) {
                    return false;
                } 
                String[] current = result[index];
                row.setCurrentRowData(current);
                return true;
    
            }
    
            ;
        }).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];
        }
    }
    

    【讨论】:

    • 你为什么要做 MockRow 类?
    • @UmerHayat - 它是对数组表示的干净封装。
    • @UmerHayat 嗨,这样应该不错吧?我必须编写特定的测试吗?
    【解决方案3】:

    我稍微改写了@karthik m 的答案,以使 ResultSet 模拟程序独立:

    通过使用下面的类,我可以轻松地将查询结果导出为 csv 并围绕它编写测试。

    并非 ResultSet 中的所有方法都被模拟,因为我不需要它们,但使用起来应该相当简单。

    import no.di.common.util.StringUtil;
    import org.apache.commons.io.FileUtils;
    import org.apache.commons.io.LineIterator;
    import org.mockito.invocation.InvocationOnMock;
    import org.mockito.stubbing.Answer;
    
    import java.io.File;
    import java.io.IOException;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.concurrent.atomic.AtomicInteger;
    
    import static org.mockito.Matchers.anyInt;
    import static org.mockito.Matchers.anyString;
    import static org.mockito.Mockito.doAnswer;
    import static org.mockito.Mockito.mock;
    
    /**
     * Creates a Mock of a ResultSet
     */
    public class ResultSetMocker {
    
        private Map<String, Integer> columnNames = new HashMap<>();
    
        private Object[][] result;
    
        public ResultSetMocker(String filename) throws IOException {
            loadData(filename);
        }
    
        private void loadData(String filename) throws IOException {
            List<Object[]> toRet = new ArrayList<>();
    
            int numberOfParts = 0;
            LineIterator it = FileUtils.lineIterator(new File(filename), "ISO8859-1");
            try {
                String names = it.nextLine();
                String[] name = names.split(";");
                for(int i = 0; i < name.length; i++) {
                    columnNames.put(name[i], i + 1);
                }
    
                while (it.hasNext()) {
                    String line = it.nextLine();
    
                    String[] parts = line.split(";");
                    numberOfParts = parts.length;
                    Object[] result = new Object[parts.length];
                    for(int i = 0; i < parts.length; i++) {
                        if(parts[i].equals("(null)"))
                            result[i] = null;
                        else if(StringUtil.isAllNumeric(parts[i]))
                            result[i] = Integer.parseInt(parts[i]);
                        else
                            result[i] = parts[i];
                    }
    
                    toRet.add(result);
                }
            } finally {
                it.close();
            }
    
            result = toRet.toArray(new Object[toRet.size()][numberOfParts]);
        }
    
        public ResultSet getResultSet() throws SQLException, IOException {
            ResultSet resultSet = mock(ResultSet.class);
    
            final AtomicInteger idx = new AtomicInteger(0);
            final MockRow row = new MockRow(columnNames);
    
            doAnswer(new Answer<Boolean>() {
                @Override
                public Boolean answer(InvocationOnMock invocation) throws Throwable {
                    int index = idx.getAndIncrement();
                    if (result.length > index) {
                        row.setCurrentRowData(result[index]);
                        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];
                    return row.getString(idx);
                }
            }).when(resultSet).getString(anyInt());
    
            doAnswer(new Answer<String>() {
                @Override
                public String answer(InvocationOnMock invocation) throws Throwable {
                    Object[] args = invocation.getArguments();
                    String name = (String) args[0];
                    return row.getString(name);
                }
            }).when(resultSet).getString(anyString());
    
            doAnswer(new Answer<Object>() {
                @Override
                public Object answer(InvocationOnMock invocation) throws Throwable {
                    Object[] args = invocation.getArguments();
                    String name = (String) args[0];
                    return row.getObject(name);
                }
            }).when(resultSet).getObject(anyString());
    
            doAnswer(new Answer<Integer>() {
                @Override
                public Integer answer(InvocationOnMock invocation) throws Throwable {
                    Object[] args = invocation.getArguments();
                    String name = (String) args[0];
                    return row.getInt(name);
                }
            }).when(resultSet).getInt(anyString());
    
            return resultSet;
        }
    
        static class MockRow {
            Object[] rowData;
            private Map<String, Integer> columnNames;
    
            public MockRow(Map<String, Integer> columnNames) {
    
                this.columnNames = columnNames;
            }
    
            public void setCurrentRowData(Object[] rowData) {
                this.rowData = rowData;
            }
    
            public String getString(int idx) {
                return (String)rowData[idx - 1];
            }
    
            public String getString(String name) {
                return (String)rowData[columnNames.get(name) - 1];
            }
    
            public Object getObject(String name) {
                return rowData[columnNames.get(name) - 1];
            }
    
            public Integer getInt(String name) {
                return (Integer)rowData[columnNames.get(name) - 1];
            }
        }
    }
    

    【讨论】:

      【解决方案4】:

      从提出这个问题的 10 年后,您很有可能会使用带有 h2 的 Spring Boot 2+ 作为内存数据库进行测试。如果是这种情况,那么您可以使用 org.h2.tools.Csv 类从 CSV 数据创建 ResultSet 实现:

      import org.h2.tools.Csv;
      
      // mock resultset
      
      String csvResults =
          "0001, John Doe\n" +
          "0002, Bob Smith\n" +
          "0003, Alice Doe\n";
      
      ResultSet rs = new Csv().read(new StringReader(csvResults), new String[] {"id", "name"});
      

      然后以任何你想要的方式使用ResultSet。这是一个 Mockito 示例,它将其作为 RowCallbackHandler 提供给 JdbcTemplate query() 调用。

      JdbcTemplate mockTemplate = mock(JdbcTemplate.class);
      
      doAnswer(ia -> {
        while (rs.next()) {
          ia.getArgument(2, RowCallbackHandler.class).processRow(rs);
        }
        return null;
      }).when(mockTemplate).query(any(String.class), any(SqlParameterSource.class), any(RowCallbackHandler.class));
      
      // change the above when() args to match how you are using JdbcTemplate
      

      【讨论】:

        【解决方案5】:

        这里的游戏有点晚了,但看起来你原来的 Mock 需要的就是这个......

        Mockito.when(resultSetMock.next()).thenReturn(true);
        

        这是必要的,以便映射器 - 无论您使用什么来映射结果集,都将知道结果集中有数据。

        【讨论】:

        • 这将使循环永远持续下去。
        • 试试这个会破坏 Mockito.when(resultSetMock.next()).thenReturn(true).thenReturn(false);
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-06
        • 1970-01-01
        • 1970-01-01
        • 2016-04-03
        • 1970-01-01
        相关资源
        最近更新 更多