Java单元测试因KeyHolder返回null失败,模拟对象未生效
单元测试中KeyHolder无法返回有效对象导致断言失败
问题描述
我正在为某应用编写新的单元测试,目前遇到瓶颈:KeyHolder无法返回有效对象,导致测试断言失败。我已在测试中添加日志,确认keyHolder已设置正确值,但defaultDao.save()似乎未使用测试中定义的keyHolder。我预期断言会通过,因为已将测试中的KeyHolder硬编码为1。
相关代码
ScriptDao类
@Repository public class ScriptDao extends DefaultDao { private static final Logger LOG = LoggerFactory.getLogger(ScriptDao.class); public Long save(ScriptModel script) { LOG.debug("saving script {}", script); String sql = "INSERT INTO " + DBTable.SCRIPT + " (script_type_id, hash, content) " + "VALUES(?, ?, ?)"; List<Object> queryParams = new LinkedList<>(); queryParams.add(script.getScriptType()); queryParams.add(script.getHash()); queryParams.add(script.getContent()); KeyHolder keyHolder = save(sql, queryParams); return keyHolder.getKey() != null ? keyHolder.getKey().longValue() : null; } }
DefaultDao类
@Repository public class DefaultDao { @Autowired protected ObjectMapper jacksonObjectMapper; @Autowired protected JdbcTemplate jdbcTemplate; public KeyHolder save(String sql, List queryParams) { Object[] parameters = queryParams.toArray(); printSQL(sql); printParams(queryParams); KeyHolder keyHolder = new GeneratedKeyHolder(); int rowAffected = jdbcTemplate.update(getPreparedStatementCreator(sql, parameters), keyHolder); getLogger().debug("operation successfully completed on {} rows", rowAffected); return keyHolder; } }
测试用例
@ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) public class ScriptDaoTest { @InjectMocks private ScriptDao scriptDao; @Mock private JdbcTemplate jdbcTemplate; @Mock private SqlRowSet sqlRowSet; @Mock private DefaultDao defaultDao; @Test void testSaveMethod() { ScriptModel script = new ScriptModel(); script.setScriptType(1); script.setHash("hash123"); script.setContent("content"); Map<String, Object> keyMap = new HashMap<String, Object>(); keyMap.put("", 1L); KeyHolder keyHolder = new GeneratedKeyHolder(); List<Map<String, Object>> generatedKeys = keyHolder.getKeyList(); generatedKeys.clear(); generatedKeys.add(keyMap); when(defaultDao.save(anyString(), anyList())).thenReturn(keyHolder); Long result = scriptDao.save(script); assertEquals(1L, result); } }
报错信息
org.opentest4j.AssertionFailedError: Expected :1 Actual :null <Click to see difference> at org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:151) at org.junit.jupiter.api.AssertionFailureBuilder.buildAndThrow(AssertionFailureBuilder.java:132) at org.junit.jupiter.api.AssertEquals.failNotEqual(AssertEquals.java:197) at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:182) at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:177) at org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:639) at com.xxxxxxx.daos.ScriptDaoTest.testSaveMethod(ScriptDaoTest.java:62) at java.lang.reflect.Method.invoke(Method.java:498) at java.util.ArrayList.forEach(ArrayList.java:1259) at java.util.ArrayList.forEach(ArrayList.java:1259) Process finished with exit code -1
解决方案
问题根源
ScriptDao继承了DefaultDao,调用save(sql, queryParams)时,实际执行的是自身继承的DefaultDao的save方法,而非测试中mock的DefaultDao实例方法。你mock的DefaultDao对象并未注入到ScriptDao中——因为ScriptDao本身就是DefaultDao的子类,不会依赖外部注入的DefaultDao实例。
修复步骤
- 移除冗余的DefaultDao mock:测试中无需
@Mock private DefaultDao defaultDao;,只需mock DefaultDao中的依赖JdbcTemplate即可。 - 模拟JdbcTemplate的update行为:直接让JdbcTemplate的update方法填充KeyHolder的值,而非mock父类的save方法。
- 正确设置KeyHolder的键名:使用数据库自增键的实际列名(比如"id"),确保
keyHolder.getKey()能正确获取值。
修改后的测试用例
@ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) public class ScriptDaoTest { @InjectMocks private ScriptDao scriptDao; @Mock private JdbcTemplate jdbcTemplate; @Test void testSaveMethod() { ScriptModel script = new ScriptModel(); script.setScriptType(1); script.setHash("hash123"); script.setContent("content"); // 模拟JdbcTemplate的update方法,填充KeyHolder的值 when(jdbcTemplate.update(any(PreparedStatementCreator.class), any(KeyHolder.class))) .thenAnswer(invocation -> { KeyHolder keyHolder = invocation.getArgument(1); // 替换为你的表主键实际列名 Map<String, Object> keyMap = new HashMap<>(); keyMap.put("id", 1L); keyHolder.getKeyList().add(keyMap); return 1; // 返回受影响行数 }); Long result = scriptDao.save(script); assertEquals(1L, result); } }
内容的提问来源于stack exchange,提问作者Thiago Apolinario Billieri
相关产品推荐
相关产品推荐

