JUnit Mockito测试遇No Statement specified异常,如何mock flush()
问题描述
在使用JUnit和Mockito进行测试时,调用batch.flush()触发了java.lang.IllegalArgumentException: No Statement specified异常,请问该如何正确mockbatch.flush()方法?
DAO类代码
class DataDao{ public void updateStatus(List<DataVO> EvntLst, String Ind, String MessageID) { try{ BatchSqlUpdate batch = new BatchSqlUpdate(getDataSource(), UPDATE_TABLE_DATA); batch.declareParameter(new SqlParameter(IND, Types.VARCHAR)); batch.declareParameter(new SqlParameter(ID, Types.BIGINT)); batch.declareParameter(new SqlParameter(RESID, Types.BIGINT)); batch.declareParameter(new SqlParameter(SMRYID,Types.BIGINT)); for (DataVO data: EvntLst){ Object[] procInd = new Object[4]; procInd[0] = Ind; procInd[1] = data.getId(); procInd[2] = data.getResourceId(); procInd[3] = data.getSummaryID(); batch.update(procInd); } batch.setBatchSize(EvntLst.size()); batch.flush(); }catch(Exception exe){ throw new Exception(uniqueMessageID + "::更新事件状态时出错"); } } }
测试类代码
@SpringBootTest(classes = DataDAOTest.class) class DataDaoTest{ @Mock Connection connection; @Mock PreparedStatementCreator psc; @Mock PreparedStatement statement; @InjectMocks DataDAO dataDAO; @Mock JdbcTemplate jdbcTemplate; @Test public void updateStatus() throws SQLException { List<DataVO> EvntLst = new ArrayList<>(); BatchSqlUpdate batch = mock(BatchSqlUpdate.class); Mockito.when(jdbcTemplate.getDataSource()).thenReturn(dataSource); Mockito.when(dataSource.getConnection()).thenReturn(connection); Mockito.when(psc.createPreparedStatement(connection)).thenReturn(statement); batch.setDataSource(dataSource); batch.setSql(UPDATE_TABLE_DATA); batch.setBatchSize(1); String Ind = "Y"; String MessageID = "xyz"; DataVO data = new DataVO(1234L, 543L, 567L, "yyyy-mm-dd hh:mm:ss", "123l", "abc", "yyyy-mm-dd hh:mm:ss", "N"); EvntLst.add(data); Object[] procInd = new Object[4]; procInd[0] = Ind; procInd[1] = data.getId(); procInd[2] = data.getResourceId(); procInd[3] = data.getSummaryID(); Mockito.when(batch.update(eq(procInd))).thenReturn(1); dataDAO.updateStatus(EvntLst, Ind, MessageID); } }
报错堆栈
java.lang.IllegalArgumentException: No Statement specified at org.springframework.util.Assert.notNull(Assert.java:201) ~[spring-core-5.3.20.jar:5.3.20] at org.springframework.jdbc.datasource.DataSourceUtils.applyTimeout(DataSourceUtils.java:334) ~[spring-jdbc-5.3.20.jar:5.3.20] at org.springframework.jdbc.core.JdbcTemplate.applyStatementSettings(JdbcTemplate.java:1470) ~[spring-jdbc-5.3.20.jar:5.3.20] at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:650) ~[spring-jdbc-5.3.20.jar:5.3.20] at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:691) ~[spring-jdbc-5.3.20.jar:5.3.20] at org.springframework.jdbc.core.JdbcTemplate.batchUpdate(JdbcTemplate.java:1034) ~[spring-jdbc-5.3.20.jar:5.3.20] at org.springframework.jdbc.object.BatchSqlUpdate.flush(BatchSqlUpdate.java:185) ~[spring-jdbc-5.3.20.jar:5.3.20] at com.uprr.mdd.event.ehms_ds_interchange.process.dao.impl.DataDAO.updateStatus(DataDAO.java:98) [classes/:?] at com.uprr.mdd.event.ehms_ds_interchange.process.dao.DataDAOTest.updateStatus(DataDAOTest.java:129)
问题原因
测试类中mock的BatchSqlUpdate对象完全没被用到——DataDao的updateStatus方法内部是直接new了一个真实的BatchSqlUpdate实例,这个真实实例缺少必要的Statement配置,执行flush时就会抛出异常。
解决方案
方案1:重构DAO类,抽离BatchSqlUpdate创建逻辑(推荐)
把BatchSqlUpdate的创建逻辑抽成可替换的方法,测试时用Mockito的spy替换该方法返回mock对象:
修改后的DAO类
class DataDao{ private final DataSource dataSource; private final String updateSql; // 构造函数注入依赖 public DataDao(DataSource dataSource, @Value("${sql.update-table-data}") String updateSql) { this.dataSource = dataSource; this.updateSql = updateSql; } // 抽离BatchSqlUpdate创建逻辑,供测试替换 protected BatchSqlUpdate createBatchSqlUpdate() { BatchSqlUpdate batch = new BatchSqlUpdate(dataSource, updateSql); batch.declareParameter(new SqlParameter(IND, Types.VARCHAR)); batch.declareParameter(new SqlParameter(ID, Types.BIGINT)); batch.declareParameter(new SqlParameter(RESID, Types.BIGINT)); batch.declareParameter(new SqlParameter(SMRYID,Types.BIGINT)); return batch; } public void updateStatus(List<DataVO> EvntLst, String Ind, String MessageID) { try{ BatchSqlUpdate batch = createBatchSqlUpdate(); for (DataVO data: EvntLst){ Object[] procInd = new Object[4]; procInd[0] = Ind; procInd[1] = data.getId(); procInd[2] = data.getResourceId(); procInd[3] = data.getSummaryID(); batch.update(procInd); } batch.setBatchSize(EvntLst.size()); batch.flush(); }catch(Exception exe){ throw new Exception(uniqueMessageID + "::更新事件状态时出错"); } } }
修改后的测试类
@SpringBootTest class DataDaoTest{ @InjectMocks @Spy // 用spy包装真实DAO对象,以便替换特定方法 DataDAO dataDAO; @Mock BatchSqlUpdate mockBatch; @Test public void updateStatus() throws SQLException { List<DataVO> EvntLst = new ArrayList<>(); String Ind = "Y"; String MessageID = "xyz"; DataVO data = new DataVO(1234L, 543L, 567L, "yyyy-mm-dd hh:mm:ss", "123l", "abc", "yyyy-mm-dd hh:mm:ss", "N"); EvntLst.add(data); // 让spy对象的createBatchSqlUpdate方法返回mock对象 Mockito.doReturn(mockBatch).when(dataDAO).createBatchSqlUpdate(); // 执行测试 dataDAO.updateStatus(EvntLst, Ind, MessageID); // 验证方法调用 Object[] expectedParams = new Object[]{Ind, data.getId(), data.getResourceId(), data.getSummaryID()}; Mockito.verify(mockBatch).update(expectedParams); Mockito.verify(mockBatch).flush(); } }
方案2:用PowerMock mock构造函数(不推荐,侵入性强)
如果不想修改DAO代码,可以用PowerMock拦截BatchSqlUpdate的构造函数,让new出来的实例是mock对象:
测试类代码
@RunWith(PowerMockRunner.class) @PrepareForTest(DataDao.class) // 指定要mock构造函数的类 class DataDaoTest{ @InjectMocks DataDAO dataDAO; @Mock BatchSqlUpdate mockBatch; @Test public void updateStatus() throws SQLException { List<DataVO> EvntLst = new ArrayList<>(); String Ind = "Y"; String MessageID = "xyz"; DataVO data = new DataVO(1234L, 543L, 567L, "yyyy-mm-dd hh:mm:ss", "123l", "abc", "yyyy-mm-dd hh:mm:ss", "N"); EvntLst.add(data); // mock BatchSqlUpdate的构造函数,返回mock对象 PowerMockito.whenNew(BatchSqlUpdate.class) .withAnyArguments() .thenReturn(mockBatch); // 执行测试 dataDAO.updateStatus(EvntLst, Ind, MessageID); // 验证方法调用 Object[] expectedParams = new Object[]{Ind, data.getId(), data.getResourceId(), data.getSummaryID()}; Mockito.verify(mockBatch).update(expectedParams); Mockito.verify(mockBatch).flush(); } }
内容的提问来源于stack exchange,提问作者nandini
相关产品推荐
相关产品推荐

