繁体   English   中英

为 jdbcTemplate.batchUpdate() 方法编写单元测试

[英]Write unit test for jdbcTemplate.batchUpdate() method

我有 jdbcTemplate 代码,我正在尝试在其上编写单元测试用例。


     public void updateData(List<Student> students, String status){
        try{jdbcTemplate.batchUpdate("update query", new BatchPreparedStatementSetter(){
    
            @Override
            public int getBatchSize()
              return students.size();
            }  
            @Override
            public void setValues(PreparedStatement ps int i){
              Student student = students.get(i);
              ps.setInt(1, student.getRollNo());
              ps.setString(2, student.getName());            
             }
      });
    }catch(Exception ex){}
    
 }

但问题是我无法涵盖完整的代码。 我能够覆盖到:

try{jdbcTemplate.batchUpdate("更新查询", new BatchPreparedStatementSetter(){

测试代码片段

@Test
public void testMe(){
List<Student> students = new ArrayList<>();
 mockedObject.updateData(students ,"success");

}

请帮忙。

这里的困难在于包含您要测试的主要逻辑的new BatchPreparedStatementSetter(){...}实例是updateData()方法的实现细节。 在测试方法内部定义。
要解决这个问题,您有两种经典方法:

  • 支持带有@DataJpaTest (最终是部分集成测试)的测试切片,这将更简单,因为您将能够测试副作用并且当您在数据库中断言 state 而不是代码中的语句时也更有帮助。
  • 在工厂中提取BatchPreparedStatementSetter实例创建。
    通过这种方式,您可以在单元测试中捕获它。

例如:

@Service
class BatchPreparedStatementFactory{

   public BatchPreparedStatementSetter ofStudentsBatchPreparedStatementSetter(List<Student> students, String status){

      return 
      new BatchPreparedStatementSetter(){
        
                @Override
                public int getBatchSize()
                  return students.size();
                }  
                @Override
                public void setValues(PreparedStatement ps int i){
                  Student student = students.get(i);
                  ps.setInt(1, student.getRollNo());
                  ps.setString(2, student.getName());            
                 }
          });
     }
}

现在在您的原始代码中使用它:

 // inject it
 BatchPreparedStatementFactory batchPreparedStatementFactory;

 public void updateData(List<Student> students, String status){
    try{jdbcTemplate.batchUpdate("update query", batchPreparedStatementFactory.ofStudentsBatchPreparedStatementSetter(students, status );
    }catch(Exception ex){}    
  }

现在你有两个组件和两个测试:

  • BatchPreparedStatementFactoryTest (没有模拟)测试getBatchSize()setValues() 这是非常直接的。
  • 您的初始测试(带有模拟)断言jdbcTemplate.batchUpdate()是使用预期参数调用的,特别是BatchPreparedStatementFactory.ofStudentsBatchPreparedStatementSetter(...)返回的实例。
    要执行该断言,您应该定义几个模拟: jdbcTemplateBatchPreparedStatementFactoryBatchPreparedStatementSetter

例如第二种情况:

// mock the factory return
BatchPreparedStatementSetter batchPreparedStatementSetterDummyMock = Mockito.mock(BatchPreparedStatementSetter.class);
Mockito.when(batchPreparedStatementFactoryMock.ofStudentsBatchPreparedStatementSetter(students, status))
  .thenReturn(batchPreparedStatementSetterDummyMock);

// call the method to test
updateData(students, status);

// verify that we call the factory with the expected params
 Mockito.verify(jdbcTemplateMock)
        .batchUpdate("update query", batchPreparedStatementSetterDummyMock);

就我个人而言,我不太喜欢那种过于精细的单元测试 mocking。 我会坚持使用@DataJpaTest或更多的全局集成测试来断言与 JDBC/JPA 相关的事情。

正如@davidxxx 回答的那样,通过创建工厂来重构代码是一个很好的解决方案。 如果您不想创建工厂,您可以使用下面的解决方案对batchUpdate调用中编写的逻辑进行单元测试。

import static org.mockito.Mockito.*;

@Test
public void testJDBCBatchUpdate() {
    String expectedSQL = "Select * from TableName";
    doAnswer(invocationOnMock -> {
 
        String actualSQL =invocationOnMock.getArgumentAt(0, String.class);
        assertEquals(expectedSQL, actualSQL);
        
        PreparedStatement preparedStatementMock=Mockito.mock(PreparedStatement.class);
        BatchPreparedStatementSetter setter =invocationOnMock.getArgumentAt(1, BatchPreparedStatementSetter.class);
        setter.setValues(preparedStatementMock, 0);
        
        verify(preparedStatementMock, times(1)).setObject(anyInt(), anyString());
        
        int batchSize=setter.getBatchSize();
        assertEquals(expectedBatchSize,batchSize);
        
        return null;
    }).when(jdbcTemplate).batchUpdate(anyString(), any(BatchPreparedStatementSetter.class));
    
    List<Datum> data = service.getData();
    
    verify(jdbcTemplate, times(1)).batchUpdate(anyString(),any(BatchPreparedStatementSetter.class)); 
}

暂无
暂无

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

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