Spring Boot微服务中MockWebServer未Mock服务,发起真实请求问题
问题根源分析
- 硬编码BASE_URL覆盖测试配置:UserClient构造函数里硬编码了
http://user-service作为baseUrl,不管测试时传入的WebClient.Builder设置了什么地址,都会被这个硬编码值覆盖,导致请求还是发往真实服务。 - @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
相关产品推荐
相关产品推荐

