使用Mockito模拟HttpClient.send遇空指针,需按URI返回不同响应
问题解决:Mockito模拟HttpClient根据URI返回不同响应
报错原因分析
你遇到的NullPointerException有两个核心原因:
- 匹配器未处理null参数:Mockito在匹配参数时,可能会传入null进行校验,而你的lambda直接调用
httpRequest.uri(),没有先判断请求对象是否为null,导致空指针。 - Host判断逻辑错误:
httpRequest.uri().getHost()返回的是纯主机名(如localhost.com),但你用了带协议的完整地址"http://localhost.com"做匹配,导致永远匹配失败,Mockito无法找到对应存根进而引发后续问题。
正确解决方案
步骤1:创建不同的Mock响应
针对两个接口分别生成Mock的HttpResponse,模拟不同的返回结果:
// 模拟getTokenTwo接口的响应 HttpResponse<String> mockTokenTwoResp = mock(HttpResponse.class); when(mockTokenTwoResp.statusCode()).thenReturn(200); when(mockTokenTwoResp.body()).thenReturn("fake-token-two"); // 模拟getTokenOne接口的响应 HttpResponse<String> mockTokenOneResp = mock(HttpResponse.class); when(mockTokenOneResp.statusCode()).thenReturn(200); when(mockTokenOneResp.body()).thenReturn("fake-token-one");
步骤2:针对不同URI设置存根
使用argThat匹配器,先判断请求不为null,再根据URI路径或完整地址匹配,分别返回对应的Mock响应:
HttpClient mockHttpClient = mock(HttpClient.class); // 匹配getTokenTwo方法的请求URI when(mockHttpClient.send( argThat(request -> request != null && request.uri().equals(uriTwo.resolve("/endpointAtUriTwo"))), any(HttpResponse.BodyHandler.class) )).thenReturn(mockTokenTwoResp); // 匹配getTokenOne方法的请求URI when(mockHttpClient.send( argThat(request -> request != null && request.uri().equals(uriOne.resolve("/endpointAtUriOne"))), any(HttpResponse.BodyHandler.class) )).thenReturn(mockTokenOneResp);
完整测试示例
@Test void testAuthenticationHandler() throws IOException, InterruptedException { // 初始化测试用的基础URI URI uriOne = URI.create("http://test-host/one"); URI uriTwo = URI.create("http://test-host/two"); // 创建Mock响应 HttpResponse<String> mockTokenTwoResp = mock(HttpResponse.class); when(mockTokenTwoResp.statusCode()).thenReturn(200); when(mockTokenTwoResp.body()).thenReturn("fake-token-two"); HttpResponse<String> mockTokenOneResp = mock(HttpResponse.class); when(mockTokenOneResp.statusCode()).thenReturn(200); when(mockTokenOneResp.body()).thenReturn("fake-token-one"); // 创建Mock HttpClient并设置存根 HttpClient mockHttpClient = mock(HttpClient.class); when(mockHttpClient.send( argThat(request -> request != null && request.uri().equals(uriTwo.resolve("/endpointAtUriTwo"))), any(HttpResponse.BodyHandler.class) )).thenReturn(mockTokenTwoResp); when(mockHttpClient.send( argThat(request -> request != null && request.uri().equals(uriOne.resolve("/endpointAtUriOne"))), any(HttpResponse.BodyHandler.class) )).thenReturn(mockTokenOneResp); // 初始化业务类并触发测试 AuthenticationHandler handler = new AuthenticationHandler(mockHttpClient, uriOne, uriTwo); handler.getTokenOne(); // 验证HttpClient的send方法被调用两次 verify(mockHttpClient, times(2)).send(any(HttpRequest.class), any(HttpResponse.BodyHandler.class)); }
额外提示
- 如果不需要精确匹配完整URI,也可以通过路径片段判断,比如
request.uri().getPath().endsWith("/endpointAtUriTwo"),适配更灵活的场景。 - 确保测试中传入的
uriOne和uriTwo与业务代码中使用的基础URI完全一致,否则匹配会失败。
内容的提问来源于stack exchange,提问作者Steven Spasbo
相关产品推荐
相关产品推荐

