简体   繁体   English

如何在 mockito 中使用 ArgumentCaptor

[英]How to use ArgumentCaptor in mockito

I'm learning Mockito and Unit Testing in general.我正在学习 Mockito 和一般的单元测试。 I want to learn how to unit test better by using Argument Captor.我想学习如何使用 Argument Captor 更好地进行单元测试。 I'm using jdbc to handle my SQL statement.我正在使用 jdbc 来处理我的 SQL 语句。 I have a method that inserts a user into my DB.我有一种将用户插入我的数据库的方法。

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

Below is the test that I'm trying to write with ArgumentCaptor.下面是我尝试使用 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(??));
      }

Any advice or insight on what should be in the '??'关于“??”中应该包含什么的任何建议或见解for the assert statement or if this is the correct way to use Argument Captor?对于 assert 语句,或者这是否是使用 Argument Captor 的正确方法?

Thank You谢谢你

Edit:编辑:

      @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));
          }

I like your approach of using ArgumentCaptor s.我喜欢您使用ArgumentCaptor的方法。

You are using the ArgumentCaptor correctly to capture the argument of the method update on the mocked JDBC template;您正在正确使用ArgumentCaptor来捕获模拟 JDBC 模板上方法update的参数; however, you cannot extract the argument used to call the PreparedStatementCreator , because this is object is not a mock.但是,您不能提取用于调用PreparedStatementCreator的参数,因为这是对象不是模拟。

Conceptually, the difficulty you have to test this part of your code comes from the fact you don't control the creation of the PreparedStatementCreator .从概念上讲,测试这部分代码的困难在于您无法控制PreparedStatementCreator创建 One possible solution would be to take back control on how and when you create these objects;一种可能的解决方案是收回对创建这些对象的方式和时间的控制; so as to allow you to mock them in your tests.以便您可以在测试中模拟它们。

Following a standard creational pattern , you could introduce a factory which (single) responsibility is to create PreparedStatementCreator .按照标准的创建模式,您可以引入一个工厂,其(单一)职责是创建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;
  }
}

Then, in the class you are testing (which contains the JDBC mock), you can inject a mock of the PreparedStatementCreatorFactory .然后,在您正在测试的类(包含 JDBC 模拟)中,您可以注入PreparedStatementCreatorFactory的模拟。 Then, instead of capturing the argument of the JDBC mock, you can capture the argument on the factory instead;然后,您可以捕获工厂中的参数,而不是捕获 JDBC 模拟的参数; and, of course, specify what the mocked factory returns.当然,还要指定模拟工厂返回的内容。

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

One drawback of this approach is that it adds one level of indirection and relative complexity;这种方法的一个缺点是它增加了一层间接性和相对复杂性; the main advantage is, as we see, to improve the separation of concerns in your design and in fine the testability of your code.如我们所见,主要优点是改善设计的关注点分离以及代码的可测试性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM