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

使用@RestClientTest时请求未被检测,如何测试异步RestClient服务?

问题:使用@RestClientTest测试RestClient调用时,MockRestServiceServer未检测到请求

我有一个通过默认配置的RestClient调用第三方API的服务,当使用@RestClientTest进行测试时,MockRestServiceServer未检测到任何传入请求,不确定是RestClient注入异常还是异步行为导致。请问该如何正确测试?

报错信息

java.lang.AssertionError: 
No further requests expected: HTTP GET https://api.foo.com/get-info
0 request(s) executed

相关代码

业务服务类 MyService

@Service
public class MyService {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    RestClient restClient;

    @Autowired 
    MyRepository repository;

    @Async
    public CompletableFuture<Void> fetch(Entity e) {

        List<MyDto> info = restClient
                .get()
                .uri("/get-info" + e.getId())
                .attributes(clientRegistrationId("oauth2-client"))
                .retrieve()
                .body(new ParameterizedTypeReference<List<MyDto>>() {});

        repository.saveAll(info);

        return CompletableFuture.completedFuture(null);
    }
}

测试类 MyServiceTest

@RestClientTest({MyService.class})
@Import({MyServiceTest.TestConfig.class})
class MyServiceTest {

    @TestConfiguration
    static class TestConfig {

        @Bean
        ClientRegistrationRepository clientRegistrationRepository() {
            return new InMemoryClientRegistrationRepository(ClientRegistration.withRegistrationId("oauth2-client")
                    .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
                    .clientId("test")
                    .tokenUri("test")
                    .build());
        }

        @Bean
        OAuth2AuthorizedClientService auth2AuthorizedClientService(
                ClientRegistrationRepository clientRegistrationRepository) {
            return new InMemoryOAuth2AuthorizedClientService(clientRegistrationRepository);
        }

        @Bean
        public Executor asyncTaskExecutor() {
            return new SyncTaskExecutor();
        }

        @Bean
        public RestClient restClient(RestClient.Builder restClientBuilder) {
            return restClientBuilder 
                    .baseUrl("https://api.foo.com")
                    .defaultHeader("X-API-KEY", "test-api-key")
                    .build();
        }
    }

    MockRestServiceServer server;

    @MockBean
    MyRepository myRepository;

    @Autowired
    private MyService service;

    @Autowired
    RestClient.Builder restClientBuilder;

    @BeforeEach
    void setUp() {
        server = MockRestServiceServer.bindTo(restClientBuilder).build();
    }

    @Test
    void testFetch() {
        Entity e = new Entity();
        e.setId(1L);

        server.expect(requestTo("https://api.foo.com/get-info"))
                .andRespond(
                        withSuccess().contentType(MediaType.APPLICATION_JSON));

        service.fetch(e).join();

        server.verify();
    }
}

RestClient配置类 RestClientConfig

@Configuration
public class RestClientConfig {

    @Value("${api.key}")
    private String apiKey;

    @Bean
    public OAuth2AuthorizedClientManager authorizedClientManager(
            ClientRegistrationRepository clientRegistrationRepository,
            OAuth2AuthorizedClientService authorizedClientService) {

        AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager =
                new AuthorizedClientServiceOAuth2AuthorizedClientManager(
                        clientRegistrationRepository, authorizedClientService);

        authorizedClientManager.setAuthorizedClientProvider(OAuth2AuthorizedClientProviderBuilder.builder()
                .authorizationCode()
                .build());

        return authorizedClientManager;
    }

    @Bean
    public RestClient restClient(OAuth2AuthorizedClientManager authorizedClientManager) {
        return RestClient.builder()
                .baseUrl("https://api.foo.com")
                .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
                .defaultHeader("X-API-KEY", apiKey)
                .requestInterceptor(new OAuth2ClientHttpRequestInterceptor(authorizedClientManager))
                .build();
    }
}

解决方案

问题主要由3个核心原因导致,逐一修复即可:

1. 绑定MockRestServiceServer的对象错误

测试中绑定的是RestClient.Builder,但业务实际使用的是已构建完成的RestClient实例,导致Mock服务未关联到实际被调用的对象。

修复方式:直接绑定注入的RestClient实例:

@Autowired
private RestClient restClient;

@BeforeEach
void setUp() {
    server = MockRestServiceServer.bindTo(restClient).build();
}

2. 请求URI不匹配

业务代码中拼接的URI是/get-info + e.getId(),当e.getId()为1时,实际请求地址是https://api.foo.com/get-info1,但测试里期望的是https://api.foo.com/get-info,URI不匹配导致Mock服务判定无请求。

修复测试中的期望地址:

server.expect(requestTo("https://api.foo.com/get-info1"))
        .andRespond(withSuccess().contentType(MediaType.APPLICATION_JSON));

或使用匹配器增强灵活性:

server.expect(requestTo(endsWith("/get-info1")))
        .andRespond(withSuccess().contentType(MediaType.APPLICATION_JSON));

3. RestClient配置缺失OAuth2拦截器

测试类的TestConfig中构建的RestClient未添加OAuth2ClientHttpRequestInterceptor,与业务代码中的RestClient配置不一致,可能导致请求流程异常。

修复TestConfig中的RestClient Bean:

@Bean
public RestClient restClient(RestClient.Builder restClientBuilder, OAuth2AuthorizedClientManager authorizedClientManager) {
    return restClientBuilder 
            .baseUrl("https://api.foo.com")
            .defaultHeader("X-API-KEY", "test-api-key")
            .requestInterceptor(new OAuth2ClientHttpRequestInterceptor(authorizedClientManager))
            .build();
}

额外修正点

  • 测试类中@Autowired private Service service;需改为@Autowired private MyService service;,修正类名错误。
  • 业务类中@Autowired MyRepository需补充变量名,改为@Autowired MyRepository repository;。

修复后测试类即可正确检测到请求并通过验证。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 18:01:00