使用RestHighLevelClient的indexAsync方法报空指针和参数匹配器异常如何解决
问题根因定位
1. org.mockito.exceptions.misusing.InvalidUseOfMatchersException 报错原因
- 你在打桩(when/doReturn等)或者verify验证之外的地方使用了Mockito参数匹配器(比如
any()、eq()这类方法) - 打桩/验证
indexAsync方法时参数混用了匹配器和字面量:如果某一个参数用了匹配器,其余所有固定值参数都必须用eq()包裹,否则就会触发该报错 - 打桩时参数个数和
indexAsync的实际入参不匹配:该方法标准入参为三个,分别是IndexRequest、RequestOptions、ActionListener<IndexResponse>,个数不对也会触发匹配器校验失败
2. java.lang.NullPointerException 报错原因
- 未正确给mock的
RestHighLevelClient打桩indexAsync方法的逻辑,异步回调的ActionListener没有被触发,业务代码中依赖回调结果的变量未被赋值 - mock的
RestHighLevelClient没有正确注入到业务类中,业务代码调用的是未初始化的null实例 indexAsync是无返回值的异步方法,打桩时没有主动调用ActionListener的onResponse/onFailure方法,后续依赖回调结果的逻辑未执行导致空指针
解决方案
第一步:修复参数匹配器使用问题
打桩/验证时所有参数要么全用字面量,要么固定值统一用eq()包裹,不允许混用。
错误示例:
// 错误:第二个参数是固定值,没有用eq()包裹 when(restHighLevelClient.indexAsync(any(IndexRequest.class), RequestOptions.DEFAULT, any(ActionListener.class))) .thenReturn(null);
第二步:正确打桩异步回调逻辑
因为indexAsync是无返回值的方法,需要用doAnswer打桩,同时主动触发回调执行异步逻辑:
// 正确打桩写法 doAnswer(invocation -> { // 拿到方法入参里的ActionListener实例 ActionListener<IndexResponse> listener = invocation.getArgument(2); // 构造mock的返回结果 IndexResponse mockResponse = mock(IndexResponse.class); // 按需给mock返回值设置属性 when(mockResponse.getId()).thenReturn("test_doc_id"); when(mockResponse.getResult()).thenReturn(DocWriteResponse.Result.CREATED); // 主动触发成功回调,执行业务异步逻辑 listener.onResponse(mockResponse); // 模拟异常场景就替换为下面的写法 // listener.onFailure(new RuntimeException("ES写入异常测试")); return null; }).when(restHighLevelClient).indexAsync(any(IndexRequest.class), eq(RequestOptions.DEFAULT), any(ActionListener.class));
第三步:验证调用的写法也要符合匹配器规则
如果需要验证indexAsync是否被调用,写法如下:
verify(restHighLevelClient, times(1)).indexAsync( any(IndexRequest.class), eq(RequestOptions.DEFAULT), any(ActionListener.class) );
第四步:确认mock实例注入正确
确保业务类中使用的RestHighLevelClient是你创建的mock实例:用@InjectMocks注解自动注入业务类,或者手动构造业务类时将mock的client作为参数传入,不要让业务类自行实例化RestHighLevelClient。
内容的提问来源于stack exchange,提问作者Chinni
相关产品推荐
相关产品推荐

