WireMock 入門

REST,Testing
Remote
1
03:23 PM · Dec 01 ,2025

1. 概述

WireMock 是一個用於創建樁和模擬 Web 服務庫。它構建了一個 HTTP 服務器,我們可以像連接到實際 Web 服務一樣連接到它。

當一個 WireMock 服務器處於活動狀態時,我們可以設置期望、調用服務並驗證其行為。

2. Maven 依賴項

為了充分利用 WireMock 庫,我們需要在 POM 中包含 這個依賴項:

<dependency>
    <groupId>org.wiremock</groupId>
    <artifactId>wiremock</artifactId>
    <version>3.9.1</version>
    <scope>test</scope>
</dependency>

3. 程序的服務器管理

本節將介紹如何手動配置 WireMock 服務器,即無需支持 JUnit 自動配置。我們演示了使用一個非常簡單的 stub 的用法。

3.1. 服務器設置

首先,我們實例化一個 WireMock 服務器:

WireMockServer wireMockServer = new WireMockServer(String host, int port);

如果未提供任何參數,服務器的主機默認為 localhost,服務器端口默認為 8080

然後我們可以使用兩個簡單的方法啓動和停止服務器:

wireMockServer.start();

以及:

wireMockServer.stop();

3.2. 基本用法

我們首先演示 WireMock 庫的基本用法,其中提供了一個精確 URL 的 stub,無需任何其他配置。

讓我們創建一個服務器實例:

WireMockServer wireMockServer = new WireMockServer();

WireMock 服務器必須在客户端連接之前運行:

wireMockServer.start();

然後 stub 了 Web 服務:

configureFor("localhost", 8080);
stubFor(get(urlEqualTo("/baeldung")).willReturn(aResponse().withBody("Welcome to Baeldung!")));

本教程使用 Apache HttpClient API 表示連接到服務器的客户端:

CloseableHttpClient httpClient = HttpClients.createDefault();

執行一個請求,並返回一個響應:

HttpGet request = new HttpGet("http://localhost:8080/baeldung");
HttpResponse httpResponse = httpClient.execute(request);

我們將 httpResponse 變量轉換為 String,使用一個輔助方法:

String responseString = convertResponseToString(httpResponse);

以下是該轉換輔助方法的實現:

private String convertResponseToString(HttpResponse response) throws IOException {
    InputStream responseStream = response.getEntity().getContent();
    Scanner scanner = new Scanner(responseStream, "UTF-8");
    String responseString = scanner.useDelimiter("\\Z").next();
    scanner.close();
    return responseString;
}

以下代碼驗證服務器是否收到請求到預期 URL,以及到達客户端的響應與發送的響應完全相同:

verify(getRequestedFor(urlEqualTo("/baeldung")));
assertEquals("Welcome to Baeldung!", stringResponse);

最後,我們應該停止 WireMock 服務器以釋放系統資源:

wireMockServer.stop();

4. JUnit Managed Server

This section illustrates the usage of a WireMock server with the help of JUnit 5. In JUnit 5, the @Rule annotation used in JUnit 4 is replaced by lifecycle methods, such as @BeforeEach and @AfterEach, or custom extensions.

4.1. Server Setup

Using lifecycle methods, we can manage the WireMock server’s lifecycle in JUnit test cases, ensuring the server starts before each test and stops after completion.

Similar to the programmatically managed server, we create a WireMock server as a Java object:

private WireMockServer wireMockServer;

@BeforeEach
void setup() {
    wireMockServer = new WireMockServer(WireMockConfiguration.options().port(port));
    wireMockServer.start();
    WireMock.configureFor("localhost", port);
}

@AfterEach
void teardown() {
    if (wireMockServer != null) {
        wireMockServer.stop();
    }
}

If no port is supplied, the server port will default to 8080. Server host, defaulting to localhost, and other configurations may still be specified using the Options interface.

4.2. URL Matching

After setting up a WireMock server instance, the next step is to configure a stub.

In this subsection, we’ll provide a REST stub for a service endpoint using a regular expression:

stubFor(get(urlPathMatching("/baeldung/.*"))
  .willReturn(aResponse()
  .withStatus(200)
  .withHeader("Content-Type", "application/json")
  .withBody("\"testing-library\": \"WireMock\"")));

Next, we create an HTTP client, execute a request, and capture the response:

CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet request = new HttpGet("http://localhost:8080/baeldung/wiremock");
HttpResponse httpResponse = httpClient.execute(request);
String stringResponse = convertHttpResponseToString(httpResponse);

The above code snippet uses a helper method to convert the HttpResponse to a String:

private String convertHttpResponseToString(HttpResponse httpResponse) throws IOException {
    InputStream inputStream = httpResponse.getEntity().getContent();
    return convertInputStreamToString(inputStream);
}

This in turn makes use of another private method:

private String convertInputStreamToString(InputStream inputStream) {
    Scanner scanner = new Scanner(inputStream, "UTF-8");
    String string = scanner.useDelimiter("\\Z").next();
    scanner.close();
    return string;
}

Finally, we verify the stub’s behavior using the following assertions:

verify(getRequestedFor(urlEqualTo("/baeldung/wiremock")));
assertEquals(200, httpResponse.getStatusLine().getStatusCode());
assertEquals("application/json", httpResponse.getFirstHeader("Content-Type").getValue());
assertEquals("\"testing-library\": \"WireMock\"", stringResponse);

4.3. Request Header Matching

Now we will demonstrate how to stub a REST API by matching headers.

Let’s start with the stub configuration:

stubFor(get(urlPathEqualTo("/baeldung/wiremock"))
  .withHeader("Accept", matching("text/.*"))
  .willReturn(aResponse()
  .withStatus(503)
  .withHeader("Content-Type", "text/html")
  .withBody("!!! Service Unavailable !!!")));

Similar to the preceding subsection, we illustrate HTTP interaction using the HttpClient API, with help from the same helper methods:

CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet request = new HttpGet("http://localhost:8080/baeldung/wiremock");
request.addHeader("Accept", "text/html");
HttpResponse httpResponse = httpClient.execute(request);
String stringResponse = convertHttpResponseToString(httpResponse);

The following verification and assertions confirm functions of the stub we created before:

verify(getRequestedFor(urlEqualTo("/baeldung/wiremock")));
assertEquals(503, httpResponse.getStatusLine().getStatusCode());
assertEquals("text/html", httpResponse.getFirstHeader("Content-Type").getValue());
assertEquals("!!! Service Unavailable !!!", stringResponse);

4.4. Request Body Matching

We can also use the WireMock library to stub a REST API with body matching.

Here is the configuration for a stub of this kind:

stubFor(post(urlEqualTo("/baeldung/wiremock"))
  .withHeader("Content-Type", equalTo("application/json"))
  .withRequestBody(containing("\"testing-library\": \"WireMock\""))
  .withRequestBody(containing("\"creator\": \"Tom Akehurst\""))
  .withRequestBody(containing("\"website\": \"wiremock.org\""))
  .willReturn(aResponse()
  .withStatus(200)));

Now it’s time to create a StringEntity object that will be used as the body of a request:

InputStream jsonInputStream 
  = this.getClass().getClassLoader().getResourceAsStream("wiremock_intro.json");
String jsonString = convertInputStreamToString(jsonInputStream);
StringEntity entity = new StringEntity(jsonString);

The code above uses one of the conversion helper methods defined before, convertInputStreamToString.

Here is the content of the wiremock_intro.json file on the classpath:

{
    "testing-library": "WireMock",
    "creator": "Tom Akehurst",
    "website": "wiremock.org"
}

And we can configure and run HTTP requests and responses:

CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost request = new HttpPost("http://localhost:8080/baeldung/wiremock");
request.addHeader("Content-Type", "application/json");
request.setEntity(entity);
HttpResponse response = httpClient.execute(request);

This is the testing code used to validate the stub:

verify(postRequestedFor(urlEqualTo("/baeldung/wiremock"))
  .withHeader("Content-Type", equalTo("application/json")));
assertEquals(200, response.getStatusLine().getStatusCode());

4.5. Stub Priority

The previous subsections deal with situations where an HTTP request matches only a single stub.

It’s more complicated if there is more than a match for a request. By default, the most recently added stub will take precedence in such a case.

However, users can customize that behavior to take more control of WireMock stubs.

We’ll demonstrate the operations of a WireMock server when a coming request matches two different stubs, with and without setting the priority level, at the same time.

Both scenarios will use the following private helper method:

private HttpResponse generateClientAndReceiveResponseForPriorityTests() throws IOException {
    CloseableHttpClient httpClient = HttpClients.createDefault();
    HttpGet request = new HttpGet("http://localhost:8080/baeldung/wiremock");
    request.addHeader("Accept", "text/xml");
    return httpClient.execute(request);
}

First, we configure two stubs without consideration of the priority level:

stubFor(get(urlPathMatching("/baeldung/.*"))
  .willReturn(aResponse()
  .withStatus(200));
stubFor(get(urlPathEqualTo("/baeldung/wiremock"))
  .withHeader("Accept", matching("text/.*"))
  .willReturn(aResponse()
  .withStatus(503));

Next, we create an HTTP client and execute a request using the helper method:

HttpResponse httpResponse = generateClientAndReceiveResponseForPriorityTests();

The following code validates that the last configured stub is applied regardless of the one defined before when a request matches both of them:

verify(getRequestedFor(urlEqualTo("/baeldung/wiremock")));
assertEquals(503, httpResponse.getStatusLine().getStatusCode());

Let’s move on to stubs with priority levels being set, where a lower number represents a higher priority:

stubFor(get(urlPathMatching("/baeldung/.*"))
  .atPriority(1)
  .willReturn(aResponse()
  .withStatus(200));
stubFor(get(urlPathEqualTo("/baeldung/wiremock"))
  .atPriority(2)
  .withHeader("Accept", matching("text/.*"))
  .willReturn(aResponse()
  .withStatus(503));

Now we’ll carry out the creation and execution of an HTTP request:

HttpResponse httpResponse = generateClientAndReceiveResponseForPriorityTests();

The following code validates the effect of priority levels, where the first configured stub is applied instead of the last:

verify(getRequestedFor(urlEqualTo("/baeldung/wiremock")));
assertEquals(200, httpResponse.getStatusLine().getStatusCode());

5. 結論

本文介紹了 WireMock 以及如何使用各種技術(包括 URL、請求頭和請求體匹配)設置和配置該庫,用於測試 REST API。

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

發佈 評論

Some HTML is okay.