如何在mockito中使用ArgumentCaptor

时间:2018-03-19 02:39:17

标签: java mockito

我正在学习Mockito和单元测试。我想通过使用Argument Captor来学习如何更好地进行单元测试。我正在使用jdbc来处理我的SQL语句。我有一个方法将用户插入我的数据库。

public void insert(User user) {
   String sql = "INSERT INTO user (id) VALUES ?";
   jdbcTemplate.update(new PreparedStatementCreator() {
     @Override
     public PreparedStatement createPreparedStatement(Connection connection) {
       final PreparedStatement ps = connection.prepareStatement(sql);
       ps.setString(1, user.getId().trim());
       return ps;
     }
   });
 }

以下是我尝试使用ArgumentCaptor编写的测试。

    @Test
      public void testInsert() {
        User user = new User("testID");
        ArgumentCaptor<PreparedStatementCreator> captor = ArgumentCaptor.forClass(PreparedStatementCreator.class);
        insert(user);
        verify(mockJdbc, times(1)).update(captor.capture());
        PreparedStatementCreator actual = captor.getValue();
        assertEquals(??, actual.createPreparedStatement(??));
      }

对“??”应该是什么的任何建议或见解对于断言语句或者这是否是使用Argument Captor的正确方法?

谢谢

编辑:

      @Test
      public void testInsert() throws SQLException {
            ArgumentCaptor<PreparedStatementCreator> captor = ArgumentCaptor.forClass(PreparedStatementCreator.class);
            PreparedStatement ps = mockConnection.prepareStatement("INSERT INTO user (id) VALUES ?";);
            ps.setString(1, user.getId().trim());
            insert(user);
            verify(mockJdbcTemplate, times(1)).update(captor.capture());
            PreparedStatementCreator actual = captor.getValue();
            assertEquals(ps, actual.createPreparedStatement(mockConnection));
          }

1 个答案:

答案 0 :(得分:0)

我喜欢您使用ArgumentCaptor的方法。

您正在使用ArgumentCaptor正确捕获模拟JDBC模板上方法update的参数;但是,您无法提取用于调用PreparedStatementCreator的参数,因为这是对象不是模拟。

从概念上讲,测试这部分代码的难度来自于您无法控制PreparedStatementCreator创建这一事实。一种可能的解决方案是收回对创建这些对象的方式和时间的控制;以便让你在测试中模仿它们。

遵循标准creational pattern,您可以引入一个工厂(单一)负责创建PreparedStatementCreator

interface PreparedStatementCreatorFactory {

  PreparedStatementCreator newPreparedStatementCreator(Connection connection, String sql, User user);

}

public final class DefaultPreparedStatementCreatorFactory {
  @Override
  public PreparedStatementCreator newPreparedStatementCreator(Connection connection, String sql, User user) {
    final PreparedStatement ps = connection.prepareStatement(sql);
    ps.setString(1, user.getId().trim());
    return ps;
  }
}

然后,在您正在测试的类中(包含JDBC模拟),您可以注入模拟PreparedStatementCreatorFactory。然后,您可以捕获工厂中的参数,而不是捕获JDBC mock的参数;当然,还要指明模拟工厂返回的内容。

PreparedStatementCreatorFactory factory = Mockito.mock(PreparedStatementCreatorFactory.class);
PreparedStatementCreator creator = Mockito.mock(PreparedStatementCreator.class);
// Mock the creator at your convenience.

when(factory.newPreparedStatementCreator(any(Connection.class), any(String.class), any(User.class)).thenReturn(creator);

...

User user = new User("testID");
ArgumentCaptor<Connection> connectionCaptor = ArgumentCaptor.forClass(Connector.class);
ArgumentCaptor<String> sqlCaptor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);

insert(user);

verify(factory, times(1)).newPreparedStatementCreator(connectionCaptor.capture(), sqlCaptor.capture(), userCaptor.capture());

assertEquals(user, userCaptor.getValue());

这种方法的一个缺点是它增加了一个级别的间接和相对复杂性;正如我们所看到的,主要优点是改善设计中关注点的分离,以及精细代码的可测试性。