测试依赖RestClient Bean的MetadataService时无法Mock该Bean的问题
RestClient依赖类测试方案:Mock修复与@RestClientTest用法
问题背景
现有两个类:
- RestClient配置类
RestClientConfig(含Kerberos认证逻辑):
@Configuration public class RestClientConfig { //实际类更复杂,包含Kerberos认证及生成Kerberos令牌注入RestClient构建器的逻辑 @Bean("customRestClient") public RestClient restClient(RestClient.Builder builder) { return builder.build(); } }
- 依赖该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()定义返回内容- 无需手动Mock
RestClient实例,Spring会自动注入配置好的测试用RestClient
内容的提问来源于stack exchange,提问作者kataraaaa
相关产品推荐
相关产品推荐

