我正在学习 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/