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

如何用Mockito验证WebClient中Lambda表达式传入的请求头参数?

问题:如何用Mockito验证Spring WebClient添加的请求头?

我正在创建Spring WebClient,并为其构建器添加自定义请求头,代码如下:

@Component
public class ApiClient {

    private final WebClient client;
    
    public Mono<Optional<String>> getApiInfo(final String header) {
        return client
                .get()
                .uri(URI)
                .headers(requestHeaders -> {
                    requestHeaders.add("Header_name", "Header_Value");
                    if (StringUtils.isNotBlank(header)) {
                        requestHeaders.add("Second_header", header);
                    }
                })
                .retrieve()
                // 后续处理逻辑省略
                .bodyToMono(new ParameterizedTypeReference<Optional<String>>() {});
    }
}

我已Mock了所有构建步骤,但无法对headers()方法执行verify()操作,请问仅使用Mockito如何验证请求头已成功添加?


解决方法

WebClient的headers()方法接收的是Consumer<HttpHeaders>类型的参数(也就是你传入的lambda表达式),Mockito无法直接验证lambda内部的执行逻辑,因此需要通过捕获这个Consumer并手动执行的方式来验证请求头是否正确添加,具体步骤如下:

  1. Mock WebClient链式调用的所有中间对象:包括RequestHeadersUriSpec、RequestHeadersSpec、ResponseSpec等,确保链式调用能正常走到headers()方法。
  2. 用ArgumentCaptor捕获传入headers()的Consumer:通过捕获这个Consumer,我们可以拿到你编写的请求头设置逻辑。
  3. 执行捕获到的Consumer并验证HttpHeaders:创建真实的HttpHeaders实例,调用捕获到的Consumer,然后检查headers中的键值对是否符合预期。

测试代码示例

@ExtendWith(MockitoExtension.class)
class ApiClientTest {

    @Mock
    private WebClient webClient;
    @Mock
    private WebClient.RequestHeadersUriSpec<?> requestHeadersUriSpec;
    @Mock
    private WebClient.RequestHeadersSpec<?> requestHeadersSpec;
    @Mock
    private WebClient.ResponseSpec responseSpec;

    @InjectMocks
    private ApiClient apiClient;

    // 验证正常传入header的场景
    @Test
    void getApiInfo_withValidHeader_shouldAddBothHeaders() {
        // 模拟WebClient链式调用流程
        when(webClient.get()).thenReturn(requestHeadersUriSpec);
        when(requestHeadersUriSpec.uri(any(URI.class))).thenReturn(requestHeadersSpec);
        
        // 捕获headers()方法的Consumer参数
        ArgumentCaptor<Consumer<HttpHeaders>> headersCaptor = ArgumentCaptor.forClass(Consumer.class);
        when(requestHeadersSpec.headers(headersCaptor.capture())).thenReturn(requestHeadersSpec);
        
        when(requestHeadersSpec.retrieve()).thenReturn(responseSpec);
        // 模拟响应结果,适配业务方法的返回类型
        when(responseSpec.bodyToMono(any(ParameterizedTypeReference.class)))
                .thenReturn(Mono.just(Optional.of("sample-response")));

        // 调用目标方法
        String testHeaderValue = "test-second-header";
        apiClient.getApiInfo(testHeaderValue).block();

        // 执行捕获到的Consumer,填充HttpHeaders
        HttpHeaders actualHeaders = new HttpHeaders();
        headersCaptor.getValue().accept(actualHeaders);

        // 验证请求头
        assertEquals("Header_Value", actualHeaders.getFirst("Header_name"));
        assertEquals(testHeaderValue, actualHeaders.getFirst("Second_header"));
    }

    // 验证header为空的场景
    @Test
    void getApiInfo_withBlankHeader_shouldOnlyAddDefaultHeader() {
        // 重复链式调用模拟(可抽取为公共方法)
        when(webClient.get()).thenReturn(requestHeadersUriSpec);
        when(requestHeadersUriSpec.uri(any(URI.class))).thenReturn(requestHeadersSpec);
        
        ArgumentCaptor<Consumer<HttpHeaders>> headersCaptor = ArgumentCaptor.forClass(Consumer.class);
        when(requestHeadersSpec.headers(headersCaptor.capture())).thenReturn(requestHeadersSpec);
        
        when(requestHeadersSpec.retrieve()).thenReturn(responseSpec);
        when(responseSpec.bodyToMono(any(ParameterizedTypeReference.class)))
                .thenReturn(Mono.just(Optional.empty()));

        // 传入空header
        apiClient.getApiInfo("").block();

        // 执行Consumer并验证
        HttpHeaders actualHeaders = new HttpHeaders();
        headersCaptor.getValue().accept(actualHeaders);

        assertEquals("Header_Value", actualHeaders.getFirst("Header_name"));
        assertNull(actualHeaders.getFirst("Second_header"));
    }
}

关键说明

  • 核心思路是捕获Consumer并触发执行,因为lambda是延迟执行的,只有当WebClient真正发送请求时才会执行,但测试中我们Mock了所有调用,所以需要手动触发这个逻辑来验证请求头。
  • 要覆盖不同的分支场景(比如header为空/非空),分别编写测试用例确保所有逻辑都被验证到。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 17:53:24