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

如何对Spring Cloud Gateway的出站HTTP请求做断言测试?

测试出站HTTP请求的有效方案

你的过滤器核心是通过WebClient发起出站请求,不需要捕获GatewayFilterChain的exchange,直接针对WebClient的调用行为做断言即可,具体实现如下:

1. 核心思路

放弃对GatewayFilterChain传递的exchange做断言,转而聚焦**WebClient的实际调用细节**——因为过滤器的核心职责就是构造并发送符合规则的出站请求,验证WebClient的调用参数(请求方法、URL、是否携带body、请求头等),才是最直接有效的测试方式。

2. 使用MockWebClient实现测试

Spring提供了MockWebClient,专门用于模拟WebClient的请求并捕获调用细节,完美适配响应式场景的测试需求。

测试用例示例

import org.junit.jupiter.api.Test;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.reactive.function.client.MockWebClient;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

import java.net.URI;

import static org.mockito.Mockito.*;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;

public class CallingGlobalFilterTest {

    @Test
    void getRequest_ShouldNotSendBody() {
        // 构建MockWebClient,模拟后端响应
        MockWebClient mockWebClient = MockWebClient.builder()
                .defaultResponse(req -> Mono.just(MockWebClient.Response.create(HttpStatus.OK)))
                .build();

        CallingGlobalFilter filter = new CallingGlobalFilter(mockWebClient);

        // 模拟网关交换对象及相关依赖
        ServerWebExchange exchange = mock(ServerWebExchange.class);
        ServerHttpRequest request = mock(ServerHttpRequest.class);
        ServerHttpResponse response = mock(ServerHttpResponse.class);
        GatewayFilterChain chain = mock(GatewayFilterChain.class);

        when(exchange.getRequest()).thenReturn(request);
        when(exchange.getResponse()).thenReturn(response);
        when(exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR)).thenReturn(URI.create("http://example.com/test"));
        when(request.getMethod()).thenReturn(HttpMethod.GET);
        when(request.getBody()).thenReturn(Mono.empty());
        when(chain.filter(exchange)).thenReturn(Mono.empty());

        // 执行过滤器并验证流完成
        Mono<Void> result = filter.filter(exchange, chain);
        StepVerifier.create(result).verifyComplete();

        // 关键断言:GET请求未携带body
        mockWebClient.verify()
                .method(HttpMethod.GET)
                .uri("http://example.com/test")
                .withoutBody()
                .exchange();
    }

    @Test
    void postRequest_ShouldSendBody() {
        MockWebClient mockWebClient = MockWebClient.builder()
                .defaultResponse(req -> Mono.just(MockWebClient.Response.create(HttpStatus.OK)))
                .build();

        CallingGlobalFilter filter = new CallingGlobalFilter(mockWebClient);

        ServerWebExchange exchange = mock(ServerWebExchange.class);
        ServerHttpRequest request = mock(ServerHttpRequest.class);
        ServerHttpResponse response = mock(ServerHttpResponse.class);
        GatewayFilterChain chain = mock(GatewayFilterChain.class);

        when(exchange.getRequest()).thenReturn(request);
        when(exchange.getResponse()).thenReturn(response);
        when(exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR)).thenReturn(URI.create("http://example.com/test"));
        when(request.getMethod()).thenReturn(HttpMethod.POST);
        when(request.getHeaders().getContentType()).thenReturn(MediaType.APPLICATION_JSON);
        
        // 模拟POST请求体
        String requestBody = "{\"key\":\"value\"}";
        when(request.getBody()).thenReturn(Mono.just(
                org.springframework.core.io.buffer.DefaultDataBufferFactory.sharedInstance.wrap(requestBody.getBytes())
        ));
        when(chain.filter(exchange)).thenReturn(Mono.empty());

        Mono<Void> result = filter.filter(exchange, chain);
        StepVerifier.create(result).verifyComplete();

        // 关键断言:POST请求携带了正确的body
        mockWebClient.verify()
                .method(HttpMethod.POST)
                .uri("http://example.com/test")
                .body(requestBody, String.class)
                .exchange();
    }
}

3. 方案优势

  • 精准匹配测试目标:直接验证过滤器构造的出站请求是否符合规则,避免了对exchange无关属性的无效断言。
  • 适配响应式场景:MockWebClient和StepVerifier完美支持Reactor异步流的测试,不会出现时序问题。
  • 测试逻辑清晰:用例聚焦核心规则(GET忽略body、POST携带body),便于维护和扩展。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 02:52:17