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

测试依赖RestClient Bean的MetadataService时无法Mock该Bean的问题

RestClient依赖类测试方案:Mock修复与@RestClientTest用法

问题背景

现有两个类:

  1. RestClient配置类RestClientConfig(含Kerberos认证逻辑):
@Configuration
public class RestClientConfig {
  //实际类更复杂,包含Kerberos认证及生成Kerberos令牌注入RestClient构建器的逻辑
  @Bean("customRestClient")
  public RestClient restClient(RestClient.Builder builder) {
    return builder.build();
  }
}
  1. 依赖该RestClient的服务类MetadataService:
@Component
public class MetadataService {
  @Autowired 
  @Qualifier("customRestClient")
  RestClient customRestClient;
  
  public void makeCall() {
     try {
         final String response = customRestClient.method(HttpMethod.POST).uri("http://example.com")
                                      .headers(getHeaders()).body(/* some body here */).retrieve()
                                      .toEntity(String.class).getBody();
         log.info(response);
     } catch (Exception e) {
         // 异常处理
     }
  }
  
  // 假设getHeaders方法实现如下
  private HttpHeaders getHeaders() {
      HttpHeaders headers = new HttpHeaders();
      headers.setContentType(MediaType.APPLICATION_JSON);
      return headers;
  }
}

测试时遇到两个问题:

  • 用@Mock模拟RestClient时出现空指针异常:
    java.lang.NullPointerException: 无法调用"org.springframework.web.client.RestClient$RequestBodyUriSpec.uri(String, Object[])",因为"org.springframework.web.client.RestClient.method(org.springframework.http.HttpMethod)"的返回值为null
    
  • 不清楚@RestClientTest的具体用法

方案1:修复Mock测试的空指针问题

空指针原因是RestClient采用链式调用,每个方法返回的内部Spec对象都需要单独Mock,不能直接连缀写when语句(因为method()返回的对象默认是null,后续调用uri()自然报错)。

修正后的测试类代码:

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 org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestClient;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
public class MetadataServiceTest {

    @Mock
    private RestClient customRestClient;
    // 链式调用中每个返回的Spec都需要Mock
    @Mock
    private RestClient.RequestBodyUriSpec requestBodyUriSpec;
    @Mock
    private RestClient.RequestBodySpec requestBodySpec;
    @Mock
    private RestClient.ResponseSpec responseSpec;

    @InjectMocks
    private MetadataService metadataService;

    @Test
    public void testMakeCall() {
        // 模拟返回值
        ResponseEntity<String> mockResponse = new ResponseEntity<>("success", HttpStatus.OK);

        // 逐级Mock链式调用的每个步骤
        when(customRestClient.method(eq(HttpMethod.POST))).thenReturn(requestBodyUriSpec);
        when(requestBodyUriSpec.uri(eq("http://example.com"))).thenReturn(requestBodySpec);
        when(requestBodySpec.headers(any())).thenReturn(requestBodySpec);
        when(requestBodySpec.body(any())).thenReturn(requestBodySpec);
        when(requestBodySpec.retrieve()).thenReturn(responseSpec);
        when(responseSpec.toEntity(eq(String.class))).thenReturn(mockResponse);

        // 执行测试
        metadataService.makeCall();
    }
}

关键说明

  • 必须为链式调用中的每个中间对象(RequestBodyUriSpec、RequestBodySpec、ResponseSpec)创建Mock实例
  • 按调用顺序依次Mock每个方法的返回值,确保每一步都不会返回null

方案2:使用@RestClientTest进行集成测试

@RestClientTest是Spring Boot提供的专门用于测试RestClient相关组件的注解,它会自动配置RestClient的基础环境,并提供MockRestServiceServer来模拟HTTP请求和响应,无需手动Mock每个Spec对象。

测试类示例

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.client.RestClientTest;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.test.web.client.MockRestServiceServer;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.*;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;

@RestClientTest(MetadataService.class)
public class MetadataServiceRestClientTest {

    @Autowired
    private MetadataService metadataService;

    @Autowired
    private MockRestServiceServer mockServer;

    @Test
    public void testMakeCall() {
        // 模拟外部接口的响应
        mockServer.expect(requestTo("http://example.com"))
                .andExpect(method(HttpMethod.POST))
                .andExpect(content().contentType(MediaType.APPLICATION_JSON))
                .andRespond(withSuccess("success", MediaType.TEXT_PLAIN));

        // 执行测试
        metadataService.makeCall();

        // 验证请求是否被触发
        mockServer.verify();
    }
}

关键说明

  • @RestClientTest(MetadataService.class):指定要测试的服务类,Spring会自动配置相关的RestClient环境
  • MockRestServiceServer:用于模拟外部HTTP服务的请求和响应,通过expect()定义预期请求,andRespond()定义返回内容
  • 无需手动MockRestClient实例,Spring会自动注入配置好的测试用RestClient

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 15:23:19