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

如何Mock服务类构造函数内创建的WebClient?

单元测试Mock WebClient的解决方案(不使用构造注入/ReflectionTestUtils)

针对你提供的Service类,这里有两种比ReflectionTestUtils更优雅的Mock方案:

方案1:提取WebClient创建逻辑为可扩展方法

将原类中创建WebClient的逻辑抽成包私有或protected方法,测试时通过重写该方法返回Mock对象,无需破坏封装。

修改原Service类

@Service
public class Service {
  private final WebClient webClient;

  public Service(String baseUrl) {
    this.webClient = createWebClient(baseUrl);
  }

  // 包私有方法,供测试重写
  WebClient createWebClient(String baseUrl) {
    return WebClient.builder().baseUrl(baseUrl).build();
  }

  public User getUser() {
    // 补充retrieve()方法,原代码遗漏该调用环节
    return webClient.get().retrieve().bodyToMono(User.class).block();
  }
}

测试实现(Mockito Spy方式)

@ExtendWith(MockitoExtension.class)
public class ServiceTest {
  @Mock
  private WebClient mockWebClient;
  @Mock
  private WebClient.RequestHeadersUriSpec<?> requestHeadersUriSpec;
  @Mock
  private WebClient.ResponseSpec responseSpec;

  @Test
  void testGetUser() {
    // 创建Service的Spy实例,重写createWebClient方法返回Mock对象
    Service service = Mockito.spy(new Service("http://dummy-url"));
    Mockito.doReturn(mockWebClient).when(service).createWebClient(Mockito.anyString());

    // 模拟WebClient调用链
    Mockito.when(mockWebClient.get()).thenReturn(requestHeadersUriSpec);
    Mockito.when(requestHeadersUriSpec.retrieve()).thenReturn(responseSpec);
    Mockito.when(responseSpec.bodyToMono(User.class))
            .thenReturn(Mono.just(new User("testUser")));

    User result = service.getUser();
    assert result != null;
    assert result.getName().equals("testUser");
  }
}

也可以直接继承Service类编写测试子类,跳过Mockito依赖:

public class ServiceTest {
  private WebClient mockWebClient = Mockito.mock(WebClient.class);
  // 初始化其他Mock对象...

  @Test
  void testGetUser() {
    Service testService = new Service("http://dummy-url") {
      @Override
      WebClient createWebClient(String baseUrl) {
        return mockWebClient;
      }
    };

    // 模拟调用链并执行断言,逻辑同Spy方式
  }
}

方案2:使用WireMock模拟真实HTTP服务

如果不想修改原Service类,可以用WireMock启动本地模拟HTTP服务,让WebClient调用该服务地址,模拟后端响应。这种方式更贴近真实场景,无需Mock WebClient本身。

测试实现

@ExtendWith(WireMockExtension.class)
public class ServiceTest {
  @Test
  void testGetUser(@WireMockTestServer WireMockServer wireMockServer) {
    // 配置WireMock返回模拟的JSON响应
    wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo("/"))
            .willReturn(WireMock.aResponse()
                    .withHeader("Content-Type", "application/json")
                    .withBody("{\"name\":\"testUser\"}")));

    // 创建Service时传入WireMock的服务地址
    Service service = new Service(wireMockServer.baseUrl());
    User result = service.getUser();

    assert result != null;
    assert result.getName().equals("testUser");
  }
}

方案优势对比

  • 相比ReflectionTestUtils,这两种方案都不依赖类的内部字段结构,避免了反射带来的封装破坏和维护风险;
  • 方案1利用面向对象的扩展特性,符合开闭原则;
  • 方案2模拟真实HTTP交互,测试可信度更高,同时无需修改业务代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 04:56:30