Mockito测试Spring Boot服务无代码覆盖率问题及正确写法求解
问题原因
你的测试用例没有覆盖到代码,核心是测试代码本身存在大量语法/用法错误,根本没有正常执行到被测服务的真实逻辑,具体问题如下:
- 基础语法笔误导致代码无法正常编译/运行:
- 测试方法返回值错写为
voi,正确应为void - 数值类名拼写错误:
Bigecimal正确为BigDecimal - 响应对象变量名前后不一致:定义时命名为
rsp,后续赋值、传参时错写为不存在的vetResp - 空catch块吞掉了所有运行时异常,就算代码抛错你也看不到任何报错信息,自然不知道逻辑根本没走通
- 测试方法返回值错写为
- Mockito用法错误:
- 打桩语法写错:
doReturn(result).when(myRepository.update(data))是非法写法,doReturn().when()的语法要求when()内只传入mock对象本身,对应的方法调用要写在when()代码块之后 - 注解使用逻辑错误:
@Spy注解修饰的对象不会自动注入@Mock标记的依赖,默认调用无参构造生成的实例中myRepo字段为null,调用时直接触发空指针,又被你的空catch吞掉,根本执行不到后续业务逻辑。你替换成@InjectMocks后仍无覆盖,本质是前面的语法、打桩错误没有解决,不是注解本身的问题
- 打桩语法写错:
- 额外注意:你贴出的业务代码本身也存在笔误,比如
myRepo.update(ata)中的ata是未定义变量,应为方法内构造的data;@Autowire、Logger初始化类名、异常日志文本也存在拼写错误,这些问题也会导致代码无法正常运行,需要先修正。
可直接运行的覆盖测试示例
首先先修正MyService中的明显笔误,确保业务代码可正常编译:
@Service @Transactional public class MyService { private static final Logger log = LoggerFactory.getLogger(MyService.class); @Autowired private MyRepository myRepo; public Map<String, Object> myMethod(Response rsp, Request req) { Map<String, Object> data = new HashMap<String, Object>(); data.put("PROCESS_I", req.getProcessI()); data.put("REQUEST_I", req.getRequestI()); data.put("TYPE_COE", req.getTypeCoe()); data.put("STATUS_COE", rsp.getStatusCoe()); Map<String, Object> result = null; try { result = myRepo.update(data); } catch (Exception e) { log.error("Error occurred during update : " + e.getMessage()); } return result; } }
对应的测试类覆盖正常执行、异常抛出两个场景,即可覆盖该方法全部代码行:
@RunWith(MockitoJUnitRunner.class) public class MyServiceTest { @Mock private MyRepository myRepository; // 用@InjectMocks自动把mock的myRepository注入到真实MyService实例中 @InjectMocks private MyService myService; @Test public void testMyMethod_Success() { // 构造请求、响应测试参数 Request req = new Request(); req.setProcessI(new BigDecimal("43850")); req.setRequestI(new BigDecimal("146717")); req.setTypeCoe("ABC"); Response rsp = new Response(); rsp.setStatusCoe("N"); // 构造mock返回值 Map<String, Object> mockResult = new HashMap<>(); mockResult.put("updateCount", 1); // 正确打桩:任意Map参数传入update时返回预设结果 when(myRepository.update(any(Map.class))).thenReturn(mockResult); // 调用被测方法 Map<String, Object> result = myService.myMethod(rsp, req); // 断言返回结果符合预期 assertEquals(1, result.get("updateCount")); // 验证repository方法被正确调用 verify(myRepository, times(1)).update(any(Map.class)); // 如需校验传入update的参数是否组装正确,可用ArgumentCaptor捕获后断言 ArgumentCaptor<Map> paramCaptor = ArgumentCaptor.forClass(Map.class); verify(myRepository).update(paramCaptor.capture()); Map passedParam = paramCaptor.getValue(); assertEquals(new BigDecimal("43850"), passedParam.get("PROCESS_I")); assertEquals("N", passedParam.get("STATUS_COE")); } @Test public void testMyMethod_WhenRepoThrowException() { // 构造请求、响应测试参数 Request req = new Request(); req.setProcessI(new BigDecimal("43850")); req.setRequestI(new BigDecimal("146717")); req.setTypeCoe("ABC"); Response rsp = new Response(); rsp.setStatusCoe("N"); // 打桩:调用update时抛出异常,覆盖catch分支逻辑 when(myRepository.update(any(Map.class))).thenThrow(new RuntimeException("db connection error")); // 调用被测方法 Map<String, Object> result = myService.myMethod(rsp, req); // 断言异常场景返回null assertNull(result); verify(myRepository, times(1)).update(any(Map.class)); } }
注意点
- 测试代码里不要写空catch块吞异常,否则出错了完全没有排查线索
- 测普通服务方法不需要用
@Spy,@InjectMocks会创建真实的服务实例,同时把所有@Mock的依赖注入进去,调用方法时会走真实的业务逻辑,完全满足单元测试隔离外部依赖的需求 - 打桩时如果不需要精确匹配参数,用
any()系列的参数匹配器即可;如果要校验方法内部参数组装是否正确,用ArgumentCaptor捕获参数后断言即可。
内容的提问来源于stack exchange,提问作者ITMan
相关产品推荐
相关产品推荐

