知識庫 / Spring RSS 訂閱

Spring 中模擬 WebClient

Spring,Testing
HongKong
9
01:09 PM · Dec 06 ,2025

1. 概述

目前,我們的大部分服務都依賴於調用 REST API。Spring 提供了幾種構建 REST 客户端的方法,並且 WebClient 推薦使用

在本快速教程中,我們將學習如何 對使用 WebClient 調用 API 的服務進行單元測試

2. 模擬(Mocking)

我們測試中主要有兩種模擬方案:

  • 使用 Mockito 模擬 WebClient 的行為
  • 使用真實的 WebClient,並通過使用 MockWebServer (okhttp) 模擬其調用的服務

3. 使用 Mockito

Mockito 是 Java 中最常用的 Mock 庫。它擅長提供預定義的響應,但當 Mock 帶有 Fluent API 時,會變得複雜。這是因為 Fluent API 中,大量的對象會傳遞在調用代碼和 Mock 之間。

例如,我們有一個 EmployeeService 類,該類通過使用 WebClient 方法調用 HTTP 檢索數據:

public class EmployeeService {

    public EmployeeService(String baseUrl) {
        this.webClient = WebClient.create(baseUrl);
    }
    public Mono<Employee> getEmployeeById(Integer employeeId) {
        return webClient
                .get()
                .uri("http://localhost:8080/employee/{id}", employeeId)
                .retrieve()
                .bodyToMono(Employee.class);
    }
}

我們可以使用Mockito來模擬(mock)這個。

@ExtendWith(MockitoExtension.class)
public class EmployeeServiceUnitTest {
   
    @Test
    void givenEmployeeId_whenGetEmployeeById_thenReturnEmployee() {

        Integer employeeId = 100;
        Employee mockEmployee = new Employee(100, "Adam", "Sandler", 
          32, Role.LEAD_ENGINEER);
        when(webClientMock.get())
          .thenReturn(requestHeadersUriSpecMock);
        when(requestHeadersUriMock.uri("/employee/{id}", employeeId))
          .thenReturn(requestHeadersSpecMock);
        when(requestHeadersMock.retrieve())
          .thenReturn(responseSpecMock);
        when(responseMock.bodyToMono(Employee.class))
          .thenReturn(Mono.just(mockEmployee));

        Mono<Employee> employeeMono = employeeService.getEmployeeById(employeeId);

        StepVerifier.create(employeeMono)
          .expectNextMatches(employee -> employee.getRole()
            .equals(Role.LEAD_ENGINEER))
          .verifyComplete();
    }

}

正如我們所見,對於鏈中的每一次調用都需要提供不同的 mock 對象,需要四次不同的 when/thenReturn 調用。 這過於冗長且繁瑣。它還要求我們瞭解我們的服務如何精確地使用 WebClient 的實現細節,這使得測試方式變得脆弱。

那麼,我們如何編寫更好的 WebClient 測試呢?

4. 使用 MockWebServer

MockWebServer, 由 Square 團隊構建的小型 Web 服務器,可以接收並響應 HTTP 請求。

MockWebServer 進行交互,使我們的代碼能夠使用本地端點的真實 HTTP 調用。 這樣,我們就可以獲得測試預期的 HTTP 交互的益處,而無需應對複雜流式客户端的模擬挑戰。

使用 MockWebServer 建議由 Spring 團隊用於編寫集成測試 以獲得最佳效果。

4.1. MockWebServer 依賴項

要使用 MockWebServer,我們需要將 okhttpmockwebserver 的 Maven 依賴項添加到我們的 pom.xml 中:

<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>5.0.0-alpha.12</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>mockwebserver</artifactId>
    <version>5.0.0-alpha.12</version>
    <scope>test</scope>
</dependency>

4.2. 在我們的測試中添加 MockWebServer

讓我們使用 MockWebServer 測試我們的 EmployeeService

public class EmployeeServiceIntegrationTest {

    public static MockWebServer mockBackEnd;

    @BeforeAll
    static void setUp() throws IOException {
        mockBackEnd = new MockWebServer();
        mockBackEnd.start();
    }

    @AfterAll
    static void tearDown() throws IOException {
        mockBackEnd.shutdown();
    }
}

在上述 JUnit 測試類中,setUptearDown 方法負責創建和關閉 MockWebServer

接下來,需要將實際 REST 服務調用的端口映射到 MockWebServer 的端口:

@BeforeEach
void initialize() {
    String baseUrl = String.format("http://localhost:%s", 
      mockBackEnd.getPort());
    employeeService = new EmployeeService(baseUrl);
}

現在是時候創建一個樁(stub),以便 MockWebServer 可以響應一個 HttpRequest

4.3. 模擬響應

使用 MockWebServer 的便捷 enqueue 方法,可以在 Web 服務器上排隊一個測試響應:

@Test
void getEmployeeById() throws Exception {
    Employee mockEmployee = new Employee(100, "Adam", "Sandler", 
      32, Role.LEAD_ENGINEER);
    mockBackEnd.enqueue(new MockResponse()
      .setBody(objectMapper.writeValueAsString(mockEmployee))
      .addHeader("Content-Type", "application/json"));

    Mono<Employee> employeeMono = employeeService.getEmployeeById(100);

    StepVerifier.create(employeeMono)
      .expectNextMatches(employee -> employee.getRole()
        .equals(Role.LEAD_ENGINEER))
      .verifyComplete();
}

當實際的 API 調用從 getEmployeeById(Integer employeeId) 方法中執行,MockWebServer 將會返回已排隊的 stub。

4.4. 檢查請求

我們可能也想確保 MockWebServer 接收到了正確的 HttpRequest

MockWebServer 提供了一個方便的方法 takeRequest,該方法返回一個 RecordedRequest 實例:

RecordedRequest recordedRequest = mockBackEnd.takeRequest();
 
assertEquals("GET", recordedRequest.getMethod());
assertEquals("/employee/100", recordedRequest.getPath());

使用 RecordedRequest,我們可以驗證收到的 HttpRequest,以確保我們的 WebClient 正確地發送了它

5. 結論

在本文中,我們演示了用於模擬 WebClient 基於 REST 客户端代碼的兩種主要選項。

雖然 Mockito 能夠工作,並且對於簡單的示例來説可能是一個不錯的選擇,但推薦的方法是使用 MockWebServer

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.