Spring JUnit and Mockito - SimpleJdbcTemplate
Given a class that extends SimpleJdbcDaoSupport, how can you mock SimpleJdbcTemplate?
public class SimpleJdbcDaoSupportExtension extends SimpleJdbcDaoSupport {
public SimpleJdbcDaoSupportExtension (JdbcTemplate jdbcTemplate){
开发者_Go百科 super.setJdbcTemplate(jdbcTemplate);
}
public MyDomainObj getResult(){
SimpleJdbcTemplate sjdbc = getSimpleJdbcTemplate();
MyDomainObj result = sjdbc.query(*whatever necessary args*.);
return result;
}
}
Then, using Mockito:
public class Test {
@Mock private JdbcTemplate mockedJdbcTemplateDedendency;
private SimpleJdbcDaoSupportExtension testObj;
@Before
public void doBeforeEachTestCase() {
MockitoAnnotations.initMocks(this);
SimpleJdbcDaoSupportExtension sje = new SimpleJdbcDaoSupportExtension (mockedJdbcTemplateDedendency);
}
@Test
public final void test(){
when(mockedJdbcTemplateDedendency.query("what to query").thenReturn(new MyDomainObj());
}
}
The mocked JdbcTemplate is injected, but since the dao class relies on SimpleJdbcTemplate to make queries (for mapping to objects), and it's constructed internally by SimpleJdbcDaoSupport - mocking JdcbTemplate has no effect on the SimpleJdbcTemplate. So how to do this, when there are no public setters for it, and the only way to construst SimpleJdbcTemplate is to rely on that method, getSimpleJdbcObject()?
Instead of mocking the concrete class, you ought to be mocking an interface (which has the methods you need).
e.g.:
public class SimpleJdbcDaoSupportExtension extends SimpleJdbcDaoSupport implements MyDomainDao{
public SimpleJdbcDaoSupportExtension (JdbcTemplate jdbcTemplate){
super.setJdbcTemplate(jdbcTemplate);
}
public MyDomainObj getResult(){
SimpleJdbcTemplate sjdbc = getSimpleJdbcTemplate();
MyDomainObj result = sjdbc.query(*whatever necessary args*.);
return result;
}
}
public class Test {
@Mock private MyDomainDao myDomainDao ;
private YourController yourController;
@Before
public void doBeforeEachTestCase() {
MockitoAnnotations.initMocks(this);
yourController = new YourController(myDomainDao);
}
@Test
public final void testSomething(){
when(myDomainDao.getResult().thenReturn(new MyDomainObj());
//on to testing the usages of myDomainDao
yourController.doSomething();
//verify
verify(myDomainDao, times(2)).getResult();
}
}
Why would you mock JdbcTemplate? Use the real thing with an in-memory database like HSQL.
And one more thing. I resolved this issue by replacing SimpleJdbcTemplate with JdbcTemplate. I am kind of new to Java, crossing over from the .Net world, and originally went with SimpleJdbcTemplate only because I came across some documentation describing its usage which perfectly answered my needs. But Skaffman's comments led me to investigate JdbcTemplate more deeply, and then I realized I didn't really need SimpleJdbcTemplate.
Nevertheless, the philosophical part of the question still stands. How do you mock something that can only be created by asking the container itself? Seems to me that Spring is violating the DI principle here, by making a tight dependency on the container.
Another way that allows you to test more of the dao is to mock the connection, preparedstatement, and resultset. It's a little more work than just mocking the jdbctemplate, but will allow you to verify if the preparedstatement is setting the correct value, and if your custom row mapper is working correctly.
public class Test {
private MyDao dao;
private JdbcTemplate jdbcTemplate;
private Connection conn;
private PreparedStatement ps;
private ResultSet rs;
@Before
public void setUp() throws Exception {
dao = new MyDao();
conn = mock(Connection.class);
ps = mock(PreparedStatement.class);
rs = mock(ResultSet.class);
jdbcTemplate = new JdbcTemplate(new SingleConnectionDataSource(conn, false));
doa.setJdbcTemplate(jdbcTemplate);
}
@Test
public void test() throws Exception {
when(conn.prepareStatement(any(String.class))).thenReturn(ps);
when(ps.executeQuery()).thenReturn(rs);
// return one row
when(rs.next()).thenReturn(true).thenReturn(false);
when(rs.getInt("id")).thenReturn(1234);
when(rs.getString("name")).thenReturn("Bob");
MyDto myDto = dao.someDaoMethod(...)
// verify ParameterSource
verify(ps, times(1)).setInt(1, 1234);
// these verify if you are mapping the columns to the right object attribute.
assertEquals(1234, myDto.getId().intValue());
assertEquals("Bob", myDto.getName());
}
}
精彩评论