为 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("update query", new
BatchPreparedStatementSetter(){
测试代码片段
@Test
public void testMe(){
List<Student> students = new ArrayList<>();
mockedObject.updateData(students ,"success");
}
请帮忙。
这里的难点在于new BatchPreparedStatementSetter(){ ...}
包含你要测试的主要逻辑的实例是updateData()
方法的一个实现细节。它在测试方法中定义仅。
要解决这个问题,您有两种经典方法:
- 支持带有
@DataJpaTest
的测试片(这最终是部分集成测试),这会更简单,因为您将能够测试副作用,并且在断言数据库中的状态时也更有帮助,并且不是您代码中的语句。
- 提取工厂中的
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(...)
.
返回的实例
要执行该断言,您应该定义几个模拟:
jdbcTemplate
、BatchPreparedStatementFactory
和 BatchPreparedStatementSetter
。
例如第二种情况:
// 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);
就我个人而言,我不是那种带有太精细模拟的单元测试的忠实拥护者。我会坚持 @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));
}
我有 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("update query", new BatchPreparedStatementSetter(){
测试代码片段
@Test
public void testMe(){
List<Student> students = new ArrayList<>();
mockedObject.updateData(students ,"success");
}
请帮忙。
这里的难点在于new BatchPreparedStatementSetter(){ ...}
包含你要测试的主要逻辑的实例是updateData()
方法的一个实现细节。它在测试方法中定义仅。
要解决这个问题,您有两种经典方法:
- 支持带有
@DataJpaTest
的测试片(这最终是部分集成测试),这会更简单,因为您将能够测试副作用,并且在断言数据库中的状态时也更有帮助,并且不是您代码中的语句。 - 提取工厂中的
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(...)
.
返回的实例 要执行该断言,您应该定义几个模拟:jdbcTemplate
、BatchPreparedStatementFactory
和BatchPreparedStatementSetter
。
例如第二种情况:
// 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);
就我个人而言,我不是那种带有太精细模拟的单元测试的忠实拥护者。我会坚持 @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));
}