如何对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
相关产品推荐
相关产品推荐

