java - 如何在mockito中使用ArgumentCaptor

标签 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));
          }

最佳答案

我喜欢您使用 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 模拟)中,您可以注入(inject) PreparedStatementCreatorFactory 的模拟。然后,您可以捕获工厂上的参数,而不是捕获 JDBC 模拟的参数;当然,还要指定模拟工厂返回的内容。

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());

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

关于java - 如何在mockito中使用ArgumentCaptor,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49355067/

相关文章:

android - 尝试使用 Mockito 在 Presenter 中测试 void 方法时出现 NullPointerException

java - 将图像保存到桌面时出现问题

java - 有没有办法覆盖 Mockito 中的 doReturn ?

java - 评估传递给从另一个类调用的模拟方法的参数

java - 如何使用Gradle连接 fat jar 中的文本文件

java - Mockito.when 给出 InvalidUseOfMatchersException : Misplaced or misused argument matcher detected here

java - Mockito 通过泛型类型的构造函数参数反射创建模拟

java - 如何在设备旋转时旋转背景图像?

java - 如何在 Linux 上为 Firefox 添加 java 插件?

java - 使用后退按钮时显示标签分页不起作用