Java 如何在mockito中使用ArgumentCaptor
我正在学习Mockito和单元测试。我想学习如何使用参数捕获器更好地进行单元测试。我正在使用jdbc处理我的SQL语句。我有一个将用户插入数据库的方法Java 如何在mockito中使用ArgumentCaptor,java,mockito,Java,Mockito,我正在学习Mockito和单元测试。我想学习如何使用参数捕获器更好地进行单元测试。我正在使用jdbc处理我的SQL语句。我有一个将用户插入数据库的方法 public void insert(User user) { String sql = "INSERT INTO user (id) VALUES ?"; jdbcTemplate.update(new PreparedStatementCreator() { @Override public PreparedS
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(??));
}
@测试
公共无效测试集(){
用户=新用户(“testID”);
ArgumentCaptor=ArgumentCaptor.forClass(PreparedStatementCreator.class);
插入(用户);
验证(mockJdbc,times(1)).update(captor.capture());
PreparedStatementCreator-actual=captor.getValue();
资产质量(?),实际的.createPreparedStatement(?);
}
关于assert语句的“??”中应该包含什么内容,或者这是否是使用参数Captor的正确方法,有什么建议或见解吗
多谢各位
编辑:
@测试
public void testInsert()引发SQLException{
ArgumentCaptor=ArgumentCaptor.forClass(PreparedStatementCreator.class);
PreparedStatement ps=mockConnection.prepareStatement(“插入用户(id)值?”;);
ps.setString(1,user.getId().trim());
插入(用户);
验证(mockJdbcTemplate,times(1)).update(captor.capture());
PreparedStatementCreator-actual=captor.getValue();
assertEquals(ps,actual.createPreparedStatement(mockConnection));
}
我喜欢你使用ArgumentCaptor的方法
您正在正确使用ArgumentCaptor
捕获模拟JDBC模板上方法update
的参数;但是,您无法提取用于调用PreparedStatementCreator
的参数,因为该对象不是模拟对象
从概念上讲,测试这部分代码的困难在于您无法控制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;
}
}
然后,在您正在测试的类(包含JDBC模拟)中,您可以插入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());
PreparedStatementCreatorFactory=Mockito.mock(PreparedStatementCreatorFactory.class);
PreparedStatementCreator=Mockito.mock(PreparedStatementCreator.class);
//在你方便的时候嘲笑造物主。
当(factory.newPreparedStatementCreator(any(Connection.class)、any(String.class)、any(User.class))。然后返回(creator);
...
用户=新用户(“testID”);
ArgumentCaptor connectionCaptor=ArgumentCaptor.forClass(Connector.class);
ArgumentCaptor sqlCaptor=ArgumentCaptor.forClass(String.class);
ArgumentCaptor userCaptor=ArgumentCaptor.forClass(User.class);
插入(用户);
验证(工厂,时间(1)).newPreparedStatementCreator(connectionCaptor.capture(),sqlCaptor.capture(),userCaptor.capture());
assertEquals(user,userCaptor.getValue());
这种方法的一个缺点是增加了一级间接性和相对复杂性;正如我们所看到的,主要优点是改进了设计中关注点的分离,并提高了代码的可测试性。感谢您的输入,我想使用ArgumentCaptor的主要原因是要确保PreparedStatement是b正在使用正确的值调用,即user.getId()。您的方法还会用于此吗?@Rp5595是的,您应该能够使用userCaptor
验证用户的值。我在我的原始帖子中添加了一个编辑。是否可以从调用中捕获PreparedStatement并将其与我在本地创建的PreparedStatement进行比较?@Rp5595:不,很遗憾,这赢了“无法工作,因为PreparedStatementCreator actual
不是模拟对象,即使它被参数captor捕获。这是因为您正在测试的代码创建了此对象,这阻止了您对其进行模拟,这就是为什么我在设计中引入了此附加工厂。它可能会增加一些复杂性,但这为您提供了一个很小的解决方案。”r负责创建PreparedStatementCreator
的单一责任类。如果您想共享您正在测试的实际类,我可以添加我想到的关于如何将工厂注入其中的代码。您是否有我可以通过电子邮件与您进行沟通的电子邮件?
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());