如何Mock Spring WebClient及其builder解决Mock不生效问题
解决方案
你遇到的Mock不生效核心原因是业务代码直接调用WebClient.builder()静态方法生成实例,没有关联到你预先定义的Mock对象,可通过以下两种方案解决:
方案1:依赖注入重构(推荐)
该方案不需要修改现有测试逻辑,仅需调整业务代码的WebClient实例化方式,是Spring项目的最佳实践:
- 新增配置类将WebClient注入Spring容器:
@Configuration public class CustomWebClientConfig { @Bean public WebClient customWebClient() { return WebClient.builder() .exchangeStrategies(ExchangeStrategies.builder() .codecs(configurer -> configurer .defaultCodecs() .maxInMemorySize(16 * 1024 * 1024)) .build()) .clientConnector(new ReactorClientHttpConnector(HttpClient.newConnection().compress(true))) .build(); } }
- 业务代码中删除硬编码的WebClient builder逻辑,改用注入方式获取实例:
@Service public class YourService { // 直接注入容器中的WebClient实例 private final WebClient customWebClient; public YourService(WebClient customWebClient) { this.customWebClient = customWebClient; } // 原有调用逻辑直接使用注入的customWebClient即可 public JsonNode yourMethod() { return customWebClient .get() .uri(URLDecoder.decode(someEndpoint.replace("\"", ""), CLIENT_ENCODING)) .header(AUTHORIZATION, BEARER + accessToken) .accept(MediaType.APPLICATION_JSON) .retrieve().bodyToMono(JsonNode.class).block(); } }
- 测试时直接用
@MockBean替换容器中的WebClient实例即可,不需要处理builder逻辑:
@SpringBootTest public class YourServiceTest { @MockBean private WebClient mockWebClient; @Autowired private YourService yourService; @Test public void test() { // 直接按普通Mock逻辑处理mockWebClient的调用即可 WebClient.RequestHeadersUriSpec mockUriSpec = Mockito.mock(WebClient.RequestHeadersUriSpec.class); when(mockWebClient.get()).thenReturn(mockUriSpec); // 后续uri、header、retrieve等调用的Mock逻辑和你之前写的保持一致 } }
方案2:静态方法Mock(兼容未重构场景)
如果不能修改现有业务代码,可使用Mockito 3.4+的静态Mock能力,直接MockWebClient.builder()方法:
- 先引入
mockito-inline依赖(默认的mockito-core不支持静态Mock) - 测试用例写法如下:
@Test public void test() { // 声明WebClient静态类的Mock作用域 try (MockedStatic<WebClient> mockedWebClientStatic = Mockito.mockStatic(WebClient.class)) { // 初始化Mock的Builder和WebClient实例 WebClient.Builder mockBuilder = Mockito.mock(WebClient.Builder.class); WebClient mockWebClient = Mockito.mock(WebClient.class); // 静态方法打桩:调用WebClient.builder()返回我们的Mock Builder mockedWebClientStatic.when(WebClient::builder).thenReturn(mockBuilder); // Builder链式调用的所有方法都要打桩返回自身,避免中间返回空指针 when(mockBuilder.exchangeStrategies(any(ExchangeStrategies.class))).thenReturn(mockBuilder); when(mockBuilder.clientConnector(any(ClientHttpConnector.class))).thenReturn(mockBuilder); when(mockBuilder.build()).thenReturn(mockWebClient); // 后续按原有逻辑Mock mockWebClient的调用即可 WebClient.RequestHeadersUriSpec mockUriSpec = Mockito.mock(WebClient.RequestHeadersUriSpec.class); when(mockWebClient.get()).thenReturn(mockUriSpec); // 剩余uri、header、retrieve等调用的Mock逻辑保持不变 } }
注意:静态Mock的作用域仅限try-with-resources代码块内,不会影响其他测试用例,所有Builder链式调用的方法都必须打桩,否则会抛出空指针异常
内容的提问来源于stack exchange,提问作者KJ0797
相关产品推荐
相关产品推荐

