如何Mock会修改入参的DAO方法?解决测试分支无法覆盖问题
问题场景
单元测试中需Mock DAO层的createRecord方法,该方法实际逻辑为:创建数据库记录后,给传入的RecordDto类型入参设置主键ID,同时返回受影响行数。
当前测试仅通过when(dao.createRecord(any(RecordDto.class))).thenReturn(1);模拟了返回值,未模拟入参ID的修改操作,导致被测方法createSomething中input.getId() != null的分支代码无法被覆盖。
当前测试代码
RecordDto input = new RecordDto(); input.setId(null); input.setName("test"); when(dao.createRecord(any(RecordDto.class))).thenReturn(1); createSomething(input);
待Mock的DAO方法
public int createRecord(RecordDto input) { int updatedRowCount = 0; int primaryKey = /* 数据库调用生成主键 */; updatedRowCount += primaryKey != null ? 1 : 0; input.setId(primaryKey); // 核心逻辑:修改入参的ID return updatedRowCount; }
被测类方法
public void createSomething(RecordDto input) { int rowsUpdated = dao.createRecord(input); if (input.getId() != null) { // 该分支无法被当前测试覆盖 doSomething(); } }
解决方案
要同时模拟返回值和入参修改,可借助Mockito的Answer接口自定义Mock方法的执行逻辑,有两种常用写法:
写法1:使用thenAnswer(推荐用于有返回值的方法)
RecordDto input = new RecordDto(); input.setId(null); input.setName("test"); // 自定义Mock行为:修改入参ID并返回指定行数 when(dao.createRecord(any(RecordDto.class))) .thenAnswer(invocation -> { // 获取方法调用时传入的RecordDto参数 RecordDto dto = invocation.getArgument(0); // 模拟数据库生成的主键ID,根据实际类型调整值(如Long/Integer) dto.setId(123L); // 返回受影响行数 return 1; }); createSomething(input); // 此时input.getId()会被设置为123L,分支代码可被正常覆盖
写法2:使用doAnswer(适合void方法或特殊匹配场景)
如果需要绕过Mockito的默认匹配逻辑,或者Mock的是void方法,可改用doAnswer:
RecordDto input = new RecordDto(); input.setId(null); input.setName("test"); doAnswer(invocation -> { RecordDto dto = invocation.getArgument(0); dto.setId(123L); return 1; // 返回受影响行数,非void方法需指定返回值 }).when(dao).createRecord(any(RecordDto.class)); createSomething(input);
原理说明
通过Answer接口,你可以拦截Mock方法的调用,获取传入的参数对象,修改其属性后再返回指定结果,完全复刻真实DAO方法的行为,从而覆盖被测方法中的所有分支。
内容的提问来源于stack exchange,提问作者Tony Narvarte
相关产品推荐
相关产品推荐

