1 つの問題を解決するためにあなたの助けが必要です。DAO クラスに Role を保存するメソッドがあります。
@Repository(value="jdbcRoleDAO")
@Transactional(propagation=Propagation.SUPPORTS, readOnly=true, rollbackFor=Exception.class)
public class JdbcRoleDAO implements RoleDAO {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private DBLogger<Role> dbLogger;
/**
* @throws DublicateEntryException if object with specified unique for application field already
* exists in DB.
*/
@CacheEvict(value = "roles", allEntries = true)
@Transactional(propagation=Propagation.REQUIRED, readOnly=false, rollbackFor=Exception.class)
@Override
public Role save(final Role role) {
if (this.getRoleByName(role.getName()) != null) {
throw new DublicateEntryException("Record with specified role name already exists in application.");
}
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection connection)
throws SQLException {
PreparedStatement ps = connection.prepareStatement(SQL_INSERT_ROLE, new String[]{"ID"});
ps.setString(1, role.getName());
ps.setString(2, role.getDesription());
ps.setObject(3, (role.getParentRole()!=null)?role.getParentRole().getId():null);
ps.setString(4, role.getPrivilege().toString());
return ps;
}
}, keyHolder);
Long key = (Long) keyHolder.getKey();
if (key != null) {
role.setId(key);
} else {
role.setId(-1);
}
// Add information about operation to log table
dbLogger.logBasicOperation(role, OperationName.CREATE);
return role;
}
...
}
そして、Mockito を使用して、このメソッドの単体テストを作成したいと考えています。今のところ、次のようになります。
@RunWith(MockitoJUnitRunner.class)
public class JdbcRoleDAOTest {
private static final long TEST_ROLE_ID = 1L;
private static final int TEST_ROLE_VERSION = 7;
@Mock
private DBLogger<Role> dbLogger;
@Mock
private JdbcTemplate jdbcTemplate;
@InjectMocks
private JdbcRoleDAO roleDAO;
/**
* test role that is used in all methods.
*/
private Role role;
@Before
public void setUp(){
// Create a test role object
role = new Role();
role.setId(TEST_ROLE_ID);
role.setName("TEST");
role.setDesription("Test role");
role.setPrivilege(Privilege.DEFAULT);
role.setVersion(TEST_ROLE_VERSION);
// Return correct version of the object
Mockito.when(jdbcTemplate.queryForInt(JdbcRoleDAO.SQL_GET_VERSION, TEST_ROLE_ID))
.thenReturn(TEST_ROLE_VERSION);
}
@Test
public void testSave() {
Assert.assertNotNull(role);
roleDAO.save(role);
InOrder inOrder = Mockito.inOrder(jdbcTemplate, dbLogger);
// Verify execution of the conditions.
inOrder.verify(jdbcTemplate, Mockito.times(1)).update(Mockito.any(PreparedStatementCreator.class),
Mockito.any(KeyHolder.class));
inOrder.verify(dbLogger, Mockito.times(1)).logBasicOperation(role, OperationName.CREATE);
/*
* Expected -1 because I can't mock KeyHolder and should be returned -1 value (because
* KeyHolder will return null instead of key)
*/
Assert.assertEquals("Generated id is wrong!", -1, role.getId());
}
…
}
問題は、どういうわけか KeyHolder をモックしたいということですが、これを行う方法がわかりません。ご覧のとおり、save メソッドで KeyHolder インスタンスが作成されるようになりました。これが主な問題です。スコープ「プロトタイプ」でSpringを使用してKeyHolderを注入することを考えましたが、新しいロールを同時に保存しようとするユーザーが少ない場合、これが問題を引き起こす可能性があることを理解しています。また、私はこのようなことをしようとしました:
Mockito.when(jdbcTemplate.update(Mockito.any(PreparedStatementCreator.class),
Mockito.any(KeyHolder.class)))
.thenAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation)
throws Throwable {
Object[] arguments = invocation.getArguments();
for (int i=0; i<arguments.length; i++) {
if (arguments[i] instanceof KeyHolder) {
KeyHolder keyHolder = (KeyHolder) arguments[i];
KeyHolder spyKeyHolder = Mockito.spy(keyHolder);
Mockito.when(spyKeyHolder.getKey()).thenReturn(TEST_GENERATED_ID);
Mockito.doReturn(TEST_GENERATED_ID).when(spyKeyHolder).getKey();
}
}
return null;
}
});
しかし、これはうまくいきません。KeyHolder を save メソッドから外に移動せずにモック (またはスパイ) する方法を教えてください。これを可能にするMockitoの機能はありますか? それとも無理ですか?
前もって感謝します