Mockito异常场景下如何断言日志方法的执行及返回值
Mockito测试远程调用失败场景:解决空指针+验证日志方法
空指针的核心原因
你的空指针大概率是Mock的RemoteCall没有正确注入到Service实例——要么Service里还是用了真实的RemoteCall对象,要么Mock对象本身就是null。
完整测试示例(附代码)
先假设你的核心类结构是这样的:
// 远程调用接口 public interface RemoteCall { void callAPI() throws RemoteException; } // 待测试的Service类 public class ApiService { private final RemoteCall remoteCall; // 推荐用构造方法注入,方便测试 public ApiService(RemoteCall remoteCall) { this.remoteCall = remoteCall; } public void handleRequest() { try { remoteCall.callAPI(); } catch (RemoteException e) { logMessage("远程调用失败:" + e.getMessage()); } } // 要验证的日志方法(假设是protected) protected void logMessage(String content) { // 实际日志逻辑 } }
1. 正确Mock并注入RemoteCall
这里提供两种常用方式,任选其一即可:
方式1:注解式(JUnit 5 + Mockito)
用@Mock创建Mock对象,@InjectMocks自动注入到Service:
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) public class ApiServiceTest { @Mock private RemoteCall mockRemoteCall; @InjectMocks private ApiService apiService; @Test public void handleRequest_WhenRemoteCallFails_ShouldTriggerLogMessage() throws RemoteException { // 步骤1:设定Mock行为——调用callAPI时抛出指定异常 String errorMsg = "连接超时"; doThrow(new RemoteException(errorMsg)).when(mockRemoteCall).callAPI(); // 步骤2:调用待测试方法 apiService.handleRequest(); // 步骤3:验证logMessage是否被正确调用 // 因为logMessage是protected,需要用spy包装Service来捕获调用 ApiService spyService = spy(apiService); spyService.handleRequest(); verify(spyService).logMessage("远程调用失败:" + errorMsg); } }
方式2:手动构造注入(无注解)
如果不想用Mockito注解,直接手动创建Mock并传入Service构造方法:
import org.junit.jupiter.api.Test; import static org.mockito.Mockito.*; public class ApiServiceTest { @Test public void handleRequest_WhenRemoteCallFails_ShouldTriggerLogMessage() throws RemoteException { // 创建Mock对象 RemoteCall mockRemoteCall = mock(RemoteCall.class); // 手动注入到Service ApiService apiService = new ApiService(mockRemoteCall); // 设定Mock行为 String errorMsg = "连接超时"; doThrow(new RemoteException(errorMsg)).when(mockRemoteCall).callAPI(); // 执行测试 apiService.handleRequest(); // 验证日志方法 ApiService spyService = spy(apiService); spyService.handleRequest(); verify(spyService).logMessage("远程调用失败:" + errorMsg); } }
2. 关键注意事项
- 注入方式要匹配:如果你的Service用Setter注入RemoteCall,要确保Setter是public的,或者手动调用Setter注入Mock;构造方法注入是最稳妥的测试友好方式。
- protected方法的验证:如果logMessage是public,直接
verify(apiService)即可;如果是protected/private,必须用spy包装Service实例才能捕获调用。 - 杜绝真实对象:绝对不要在测试中new真实的RemoteCall实现类,否则会触发真实API调用,导致测试失败或空指针。
空指针排查 checklist
- 打印
remoteCall对象:如果输出是MockitoMock@xxx,说明Mock成功;如果是null,说明注入失败。 - 检查Service实例化:如果Service用了Spring的
@Autowired,单元测试里不要用Spring容器加载,改用@InjectMocks或手动注入Mock。 - 异常类型匹配:确保Mock抛出的异常和Service中catch的异常类型一致,否则不会进入logMessage分支。
内容的提问来源于stack exchange,提问作者WhiteFlower
相关产品推荐
相关产品推荐

