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

Spring Boot微服务中MockWebServer未Mock服务,发起真实请求问题

问题根源分析

  1. 硬编码BASE_URL覆盖测试配置:UserClient构造函数里硬编码了http://user-service作为baseUrl,不管测试时传入的WebClient.Builder设置了什么地址,都会被这个硬编码值覆盖,导致请求还是发往真实服务。
  2. @LoadBalanced注解的干扰:WebClientConfig中定义的WebClient.Builder带有@LoadBalanced,具备服务发现能力,就算尝试替换baseUrl,负载均衡拦截器依然会通过Eureka解析user-service服务名,指向真实实例。

解决方案

1. 重构UserClient,让baseUrl可配置

去掉硬编码的BASE_URL,改用配置属性或构造参数注入,方便测试时替换地址:

@Component
public class UserClient {
    private final WebClient webClient;

    // 通过配置属性注入baseUrl,生产环境用默认值,测试环境可覆盖
    public UserClient(WebClient.Builder webClientBuilder, @Value("${user.service.base-url:http://user-service}") String baseUrl) {
        this.webClient = webClientBuilder.baseUrl(baseUrl).build();
    }

    public Mono<UserResponse> createUser(UserRequest userRequest) {
        return webClient.post()
                .uri("/users")
                .bodyValue(userRequest)
                .retrieve()
                .bodyToMono(UserResponse.class);
    }
}

2. 测试时使用非负载均衡的WebClient.Builder

测试类里不要注入带@LoadBalanced的Builder,直接创建普通的WebClient.Builder,避免服务发现逻辑干扰:

public class UserClientTest {
    private MockWebServer mockWebServer;
    private UserClient userClient;

    @BeforeEach
    public void setup() throws IOException {
        mockWebServer = new MockWebServer();
        mockWebServer.start();

        // 创建不带负载均衡的普通Builder,设置mock服务器地址
        WebClient.Builder webClientBuilder = WebClient.builder()
                .baseUrl(mockWebServer.url("/").toString());
        
        // 传入mock地址初始化UserClient
        userClient = new UserClient(webClientBuilder, mockWebServer.url("/").toString());
    }

    @AfterEach
    public void tearDown() throws IOException {
        mockWebServer.shutdown();
    }

    @Test
    @DisplayName("Should create a new user")
    public void shouldCreateANewUser() {
        UserResponse mockResponse = new UserResponse(1L, "john_doe", "john@example.com");
        mockWebServer.enqueue(
                new MockResponse().setResponseCode(201)
                        .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                        .setBody(new Gson().toJson(mockResponse))
        );

        UserRequest userRequest = new UserRequest("john_doe", "password", "john@example.com");
        Mono<UserResponse> createUserMono = userClient.createUser(userRequest);
        StepVerifier.create(createUserMono)
                .expectNext(mockResponse)
                .verifyComplete();
    }
}

3. 可选:禁用测试环境的服务发现

如果用@SpringBootTest编写集成测试,可通过配置禁用Eureka客户端,彻底避免服务发现干扰:

@SpringBootTest(properties = {
        "eureka.client.enabled=false",
        "spring.cloud.discovery.enabled=false"
})
public class UserClientTest {
    // ... 测试代码
}

关键注意点

  • 测试时必须使用未被@LoadBalanced修饰的WebClient.Builder,否则负载均衡逻辑会优先解析服务名,忽略手动设置的baseUrl。
  • 禁止在客户端类中硬编码服务地址,用配置属性或服务发现方式,保证测试、生产环境的灵活性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 08:45:01