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

Spring Boot中Spring WebClient调用外部REST API的集成测试最佳方案

测试依赖外部REST接口的Spring Boot应用:WireMock实践

是的,WireMock是处理这类场景的最优解——当你的应用依赖外部REST接口时,必须通过Stub模拟外部服务的响应,才能保证测试不依赖外部服务的可用性,同时精准覆盖各种业务分支(成功、失败、超时等)。

一、添加依赖

使用Spring Boot官方的WireMock Starter,集成更便捷:

Maven

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-contract-stub-runner</artifactId>
    <scope>test</scope>
</dependency>
<!-- 或单独使用WireMock核心依赖 -->
<dependency>
    <groupId>com.github.tomakehurst</groupId>
    <artifactId>wiremock-jre8</artifactId>
    <scope>test</scope>
</dependency>

Gradle

testImplementation 'org.springframework.cloud:spring-cloud-starter-contract-stub-runner'
// 或单独依赖
testImplementation 'com.github.tomakehurst:wiremock-jre8'

二、测试服务层(WebClient调用逻辑)

假设你有一个服务类,负责通过WebClient调用外部接口:

示例服务类

@Service
public class ExternalApiService {
    private final WebClient webClient;

    public ExternalApiService(WebClient.Builder webClientBuilder) {
        this.webClient = webClientBuilder.baseUrl("https://external-api.com/api").build();
    }

    public Mono<String> fetchExternalData(String userId) {
        return webClient.get()
                .uri("/users/{id}", userId)
                .retrieve()
                .bodyToMono(String.class);
    }
}

服务层测试(用WireMock模拟外部接口)

import com.github.tomakehurst.wiremock.WireMockServer;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.test.StepVerifier;

import static com.github.tomakehurst.wiremock.client.WireMock.*;

public class ExternalApiServiceTest {
    private static WireMockServer wireMockServer;
    private ExternalApiService service;

    @BeforeAll
    static void setup() {
        wireMockServer = new WireMockServer(8081); // 指定端口启动WireMock
        wireMockServer.start();
        configureFor("localhost", 8081);
    }

    @BeforeEach
    void initService() {
        WebClient webClient = WebClient.builder()
                .baseUrl(wireMockServer.baseUrl())
                .build();
        service = new ExternalApiService(() -> webClient);
    }

    @AfterAll
    static void teardown() {
        wireMockServer.stop();
    }

    @Test
    void fetchExternalData_shouldReturnSuccessResponse() {
        // Stub外部接口的成功响应
        stubFor(get(urlEqualTo("/users/123"))
                .willReturn(aResponse()
                        .withStatus(200)
                        .withHeader("Content-Type", "application/json")
                        .withBody("{\"id\":\"123\",\"name\":\"John Doe\"}")));

        // 调用服务方法并验证结果
        StepVerifier.create(service.fetchExternalData("123"))
                .expectNext("{\"id\":\"123\",\"name\":\"John Doe\"}")
                .verifyComplete();

        // 验证外部接口被正确调用
        verify(getRequestedFor(urlEqualTo("/users/123")));
    }

    @Test
    void fetchExternalData_shouldHandle404Error() {
        // Stub外部接口的404响应
        stubFor(get(urlEqualTo("/users/999"))
                .willReturn(aResponse().withStatus(404)));

        StepVerifier.create(service.fetchExternalData("999"))
                .expectErrorMatches(throwable -> throwable instanceof WebClientResponseException.NotFound)
                .verify();
    }
}

三、测试控制器层(端到端请求链路)

假设你有一个RestController,调用上述服务类:

示例控制器类

@RestController
@RequestMapping("/api")
public class UserController {
    private final ExternalApiService externalApiService;

    public UserController(ExternalApiService externalApiService) {
        this.externalApiService = externalApiService;
    }

    @GetMapping("/users/{userId}")
    public Mono<String> getUserData(@PathVariable String userId) {
        return externalApiService.fetchExternalData(userId);
    }
}

控制器层集成测试(用@AutoConfigureWireMock自动配置)

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock;
import org.springframework.test.web.servlet.MockMvc;

import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest(properties = {"external.api.base-url=http://localhost:8080/mock-api"})
@AutoConfigureMockMvc
@AutoConfigureWireMock(port = 8080) // 自动启动WireMock并绑定端口
public class UserControllerTest {
    @Autowired
    private MockMvc mockMvc;

    @Test
    void getUserData_shouldReturnExternalData() throws Exception {
        // Stub外部接口的响应
        stubFor(get(urlEqualTo("/mock-api/users/123"))
                .willReturn(aResponse()
                        .withStatus(200)
                        .withHeader("Content-Type", "application/json")
                        .withBody("{\"id\":\"123\",\"name\":\"John Doe\"}")));

        // 调用本地控制器端点并验证响应
        mockMvc.perform(get("/api/users/123"))
                .andExpect(status().isOk())
                .andExpect(content().json("{\"id\":\"123\",\"name\":\"John Doe\"}"));

        // 验证WireMock收到了预期请求
        verify(getRequestedFor(urlEqualTo("/mock-api/users/123")));
    }
}

关键优势总结

  • 隔离依赖:完全脱离外部服务的状态,测试稳定可靠
  • 场景覆盖:轻松模拟各种HTTP状态码、响应体、超时等场景,覆盖业务异常分支
  • 精准验证:可以验证外部接口的调用次数、参数是否符合预期

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 14:37:11