我正在学习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));
}
答案 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());
这种方法的一个缺点是它增加了一个级别的间接和相对复杂性;正如我们所看到的,主要优点是改善设计中关注点的分离,以及精细代码的可测试性。