JUnit:使用 RowCallbackHandler 模拟 jdbcTemplate 的 query() 方法



我在Spring Boot(v.2.2.1.RELEASE(应用程序的Java类中有以下代码:

@Inject
private JdbcTemplate jdbcTemplate;
@Inject
private MyRowCallbackHandler myRowCallbackHandler;
public void myMethod() {
jdbcTemplate.query(MY_QUERY, myRowCallbackHandler);
}

JDBC模板对象是org.springframework.JDBC.core.JdbcTemplate的实现,处理程序是org.springframework.jdb c.core.RowCallbackHandler.的实现

使用JUnit版本4和Mockito,我可以模拟查询方法从数据库中检索一行或多行,从而调用处理程序的processRow((方法吗?

感谢您的帮助。

我在自己的代码中遇到了这个问题,我想在这里分享解决方案,尽管它与上面模拟jdbcTemplate的情况略有不同。

@InjectMocks
private JdbcOperationRepository jdbcOperationRepository;
@Mock
private NamedParameterJdbcTemplate mockJdbcTemplate;
@Test
public void testMyResults() {
final ResultSet mockResult1 = mock(ResultSet.class);
when(mockResult1.getString(MY_COLUMN)).thenReturn(value);
// ... other when statements to mock the returned data
doAnswer(invocation -> {
RowCallbackHandler callbackHandler = invocation.getArgument(2);
callbackHandler.processRow(mockResult1);
callbackHandler.processRow(mockResult2);
return null;
}).when(mockJdbcTemplate).query(any(), any(), any(RowCallbackHandler.class));
}

我也遇到了同样的问题。首先,您必须记住,使用Junit测试,您几乎可以模拟春季启动中的任何东西。

我正在以通用模式编写解决方案,以便每个人都能了解它是如何实现和使用的。

假设我们在BookService.java类中实现了一个函数:

@Service
public class BookService {
@Inject
private NamedParameterJdbcOperations jdbcTemplate;

@Inject
private FileHelper fileHelper;

public List<BookDTO> getBooks(Long libraryId){
String sql = fileHelper.getFileContents("SQL_FILE_PATH");

Map<String, Object> parameterMap = new HashMap<>();
if(libraryId!=null){
parameterMap.put("libraryId", libraryId);
}

List<BookDTO> books = new ArrayList<>();

jdbcTemplate.query(sql, parameterMap, rs -> {
BookDTO book = new BookDTO();
book.setId(rs.getLong("id"));
book.setName(rs.getString("name"));

if(rs.getObject("librarian") != null){
book.setLibrarian(rs.getString("librarian"));
}
books.add(book);
});

return books;
}
}

现在,我们想模拟上述服务类的jdbcTemplate.query()方法的功能。

我们的测试应该以这种模式编写:

public class BookServiceMockTest {
@Mock
private NamedParameterJdbcOperations jdbcTemplate;

@Mock
private FileHelper fileHelper;

private BookService mockBookService;

@Before
public void setup(){
mockBookService = new BookService();
// using reflectionUtils setFields of the fileHelper and jdbcTemplate.
// ....
}

@Test
public void getBooksTest(){

when(fileHelper.getFileContents("SQL_FILE_PATH")).thenReturn("SOME SQL");
doAnswer(invocation -> {
// we are going to mock ResultSet class.
ResultSet rs = Mockito.mock(ResultSet.class);
when(rs.getLong("id")).thenReturn(1L);
when(rs.getString("name")).thenReturn("Game of Thrones");

// this will mock the if() statement
when(rs.getObject("librarian")).thenReturn("John Doe");

// this will mock the actual get statement call on getString() inside the if statement
when(rs.getString("librarian")).thenReturn("John Doe");

// the argument index is important here.
// we are doing getArgument(2).
// This means the third parameter passed in the jdbcTemplate.query(Param0, Param1, Param2) in the BookService.getBooks() function.
RowCallbackHandler rch = (RowCallbackHandler) invocation.getArgument(2);
// as we are mocking only one row..
rch.processRow(rs);

/* // if we wanter two or more results:
when(rs.getLong("id")).thenReturn(1L).thenReturn(2L);
when(rs.getString("name")).thenReturn("Game of Thrones").thenReturn("Dance of the Dragon");
int n = 2; // no of rows..
for(int i=0; i<n; i++){
rch.processRow(rs);
}
*/
return null;
})
// the parameters used here are important. Any mismatch will result in unsuccessful.
.when(jdbcTemplate).query(eq("SOME SQL"), anyMap(), any(RowCallbackHandler.class));

List<BookDTO> books = mockBookService.getBooks(anyLong());

verify(jdbcTemplate, times(1)).query(eq("SOME SQL"), anyMap(), any(RowCallbackHandler.class));

assertThat(books).hasSize(1);
}
}

我希望这能满足你的需求!

最新更新