You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Mock RestHighLevelClient出现空指针异常的技术求助

问题:RestHighLevelClient模拟后触发空指针异常

尝试通过RestHighLevelClient client = mock(RestHighLevelClient.class);模拟Elasticsearch的RestHighLevelClient,但模拟后的client未正确初始化(表现为空)。执行测试代码后,在被测试类的SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);代码行触发空指针异常。问题始于修改构造函数并新增RestHighLevelClient的模拟操作,此前其他服务均正常工作。

用户提供的测试类代码:

import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.search.*;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.document.DocumentField;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockitoAnnotations;
import org.springframework.test.util.ReflectionTestUtils;

import java.io.IOException;
import java.time.Instant;
import java.util.*;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;

class RulePreviewsCollectorServiceTest {

    private String comAlias = "com";
    private String comNAlias = "comN";

    RestHighLevelClient client = mock(RestHighLevelClient.class);

    RulePreviewsCollectorService collectorService = createRulePreviewsCollectorService(Service,
            Service, Service, Service, Service, client);

    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
        when(merchSnapshot.getRulesSnapshot()).thenReturn(rulesSnapshot);
        client = mock(RestHighLevelClient.class);

        collectorService = new RulePreviewsCollectorService(Service, Service, Service,
                Service, Service, new Collector(),
                new Collector(), new Collector(), client);
        ReflectionTestUtils.setField(collectorService, "comAlias", "com");
        ReflectionTestUtils.setField(collectorService, "comNAlias", "comN");
    }

    @Test
    void testCollect() throws IOException {
       
       ActionsSearchRequest request = ActionsSearchRequest.builder()
                .merchSnapshot(merchSnapshot)
                .siteName(SiteName.COM)
                .searchPhrase("skirt")
                .build();
        SearchHit hit1 = new SearchHit(1);
        SearchHit hit2 = new SearchHit(2);

        SearchHits searchHits = new SearchHits(new SearchHit[]{hit1, hit2}, new TotalHits(2, TotalHits.Relation.EQUAL_TO), 1.0f);

        SearchResponse searchResponse = mock(SearchResponse.class);
        when(searchResponse.getHits()).thenReturn(searchHits);
        when(searchResponse.getTotalShards()).thenReturn(1);
        when(searchResponse.getSuccessfulShards()).thenReturn(1);
        when(searchResponse.getFailedShards()).thenReturn(0);
        when(searchResponse.getShardFailures()).thenReturn(null);
        when(searchResponse.isTimedOut()).thenReturn(false);
        when(client.search(any(SearchRequest.class), any(RequestOptions.class))).thenReturn(searchResponse);
                     
    private RulePreviewsCollectorService createRulePreviewsCollectorService(Service Service,
                                                                            Service Service,
                                                                            Service Service,
                                                                            Service Service,
                                                                            Service Service,
                                                                            RestHighLevelClient client) {

        return new RulePreviewsCollectorService(Service, Service, Service, Service, Service, new Collector(), new Collector(), new Collector(), client);
    }
}

问题根源

  1. 重复创建mock实例:类级别的client变量和setUp方法里重新创建的client是两个独立实例,测试方法中对类级别client设置的mock行为,并没有作用到被测试服务collectorService持有的client实例上。
  2. 初始化顺序错误:类级别的collectorService在setUp方法执行前就已创建,此时使用的是类级别client,但后续setUp又重新创建了collectorService并注入新的client,测试方法中依然操作旧的client实例,导致mock配置无效。

修复方案

  1. 移除类级别的client和collectorService初始化,将它们改为类成员变量,在setUp方法中统一初始化。
  2. 确保测试方法中对client设置mock行为时,操作的是注入到collectorService中的同一个实例。

修复后的完整测试类代码:

import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.search.*;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.document.DocumentField;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockitoAnnotations;
import org.springframework.test.util.ReflectionTestUtils;

import java.io.IOException;
import java.time.Instant;
import java.util.*;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;

class RulePreviewsCollectorServiceTest {

    private String comAlias = "com";
    private String comNAlias = "comN";

    // 仅声明成员变量,不在类级别初始化
    private RestHighLevelClient client;
    private RulePreviewsCollectorService collectorService;

    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
        // 初始化mock实例
        client = mock(RestHighLevelClient.class);
        // 为所有依赖的Service创建mock实例
        Service service1 = mock(Service.class);
        Service service2 = mock(Service.class);
        Service service3 = mock(Service.class);
        Service service4 = mock(Service.class);
        Service service5 = mock(Service.class);
        
        when(merchSnapshot.getRulesSnapshot()).thenReturn(rulesSnapshot);
        
        // 创建被测试服务,注入同一个client实例
        collectorService = new RulePreviewsCollectorService(service1, service2, service3,
                service4, service5, new Collector(),
                new Collector(), new Collector(), client);
        ReflectionTestUtils.setField(collectorService, "comAlias", "com");
        ReflectionTestUtils.setField(collectorService, "comNAlias", "comN");
    }

    @Test
    void testCollect() throws IOException {
        ActionsSearchRequest request = ActionsSearchRequest.builder()
                .merchSnapshot(merchSnapshot)
                .siteName(SiteName.COM)
                .searchPhrase("skirt")
                .build();
        SearchHit hit1 = new SearchHit(1);
        SearchHit hit2 = new SearchHit(2);

        SearchHits searchHits = new SearchHits(new SearchHit[]{hit1, hit2}, new TotalHits(2, TotalHits.Relation.EQUAL_TO), 1.0f);

        SearchResponse searchResponse = mock(SearchResponse.class);
        when(searchResponse.getHits()).thenReturn(searchHits);
        when(searchResponse.getTotalShards()).thenReturn(1);
        when(searchResponse.getSuccessfulShards()).thenReturn(1);
        when(searchResponse.getFailedShards()).thenReturn(0);
        when(searchResponse.getShardFailures()).thenReturn(null);
        when(searchResponse.isTimedOut()).thenReturn(false);
        
        // 此时操作的client是注入到collectorService的同一个实例
        when(client.search(any(SearchRequest.class), any(RequestOptions.class))).thenReturn(searchResponse);
        
        // 执行被测试方法
        collectorService.collect(request);
        
        // 可添加断言验证结果
        // assertEquals(...);
    }

    private RulePreviewsCollectorService createRulePreviewsCollectorService(Service service1,
                                                                            Service service2,
                                                                            Service service3,
                                                                            Service service4,
                                                                            Service service5,
                                                                            RestHighLevelClient client) {

        return new RulePreviewsCollectorService(service1, service2, service3, service4, service5, new Collector(), new Collector(), new Collector(), client);
    }
}

补充说明:

  • 确保所有依赖的Service类都通过mock(Service.class)创建实例,避免出现其他空指针异常。
  • 测试方法中必须执行被测试的业务方法(比如collectorService.collect(request)),否则mock配置不会被触发。

内容的提问来源于stack exchange,提问作者ReallyNicePerson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 08:35:55