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); } }
问题根源
- 重复创建mock实例:类级别的
client变量和setUp方法里重新创建的client是两个独立实例,测试方法中对类级别client设置的mock行为,并没有作用到被测试服务collectorService持有的client实例上。 - 初始化顺序错误:类级别的
collectorService在setUp方法执行前就已创建,此时使用的是类级别client,但后续setUp又重新创建了collectorService并注入新的client,测试方法中依然操作旧的client实例,导致mock配置无效。
修复方案
- 移除类级别的
client和collectorService初始化,将它们改为类成员变量,在setUp方法中统一初始化。 - 确保测试方法中对
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
相关产品推荐
相关产品推荐

