Pengantar WireMock

1. Ikhtisar

WireMock adalah perpustakaan untuk layanan web stubbing dan mocking . Itu membangun server HTTP yang dapat kita sambungkan seperti yang kita lakukan ke layanan web yang sebenarnya.

Saat server WireMock sedang beraksi, kita dapat mengatur ekspektasi, memanggil layanan, dan kemudian memverifikasi perilakunya.

2. Ketergantungan Maven

Agar dapat memanfaatkan pustaka WireMock, kita perlu menyertakan dependensi berikut di POM:

 com.github.tomakehurst wiremock 1.58 test 

3. Server yang Dikelola Secara Programatis

Bagian ini akan membahas cara mengkonfigurasi server WireMock secara manual. yaitu tanpa dukungan konfigurasi otomatis JUnit. Penggunaannya ditunjukkan dengan rintisan yang sangat sederhana.

3.1. Penyiapan Server

Server WireMock dapat dibuat seperti ini:

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

Jika tidak ada argumen yang diberikan, host server default ke localhost dan port server ke 8080 .

Server kemudian dapat dimulai dan dihentikan menggunakan dua metode sederhana:

wireMockServer.start();

Dan:

wireMockServer.stop();

3.2. Penggunaan Dasar

Pustaka WireMock pertama-tama akan didemonstrasikan oleh penggunaan dasar, di mana tersedia rintisan untuk URL yang tepat tanpa konfigurasi lebih lanjut. Mari buat instance server:

WireMockServer wireMockServer = new WireMockServer();

Server WireMock harus berjalan sebelum klien menghubungkannya:

wireMockServer.start();

Layanan web kemudian dihentikan:

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

Tutorial ini menggunakan Apache HttpClient API untuk mewakili klien yang terhubung ke server:

CloseableHttpClient httpClient = HttpClients.createDefault();

Permintaan dieksekusi dan tanggapan dikembalikan, masing-masing, setelah itu:

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

Kami akan mengubah variabel httpResponse menjadi String menggunakan metode helper:

String responseString = convertResponseToString(httpResponse);

Berikut adalah implementasi dari metode pembantu konversi:

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; }

Kode berikut memverifikasi bahwa server telah mendapat permintaan ke URL yang diharapkan dan respons yang tiba di klien persis seperti yang dikirim:

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

Terakhir, server WireMock harus dihentikan untuk melepaskan sumber daya sistem:

wireMockServer.stop();

4. Server yang Dikelola JUnit

Berbeda dengan bagian 3, bagian ini menggambarkan penggunaan server WireMock dengan bantuan JUnit Rule .

4.1. Penyiapan Server

Server WireMock dapat diintegrasikan ke dalam kasus uji JUnit dengan menggunakan anotasi @Rule . Hal ini memungkinkan JUnit untuk mengelola siklus proses, memulai server sebelum setiap metode pengujian dan menghentikannya setelah metode kembali.

Mirip dengan server yang dikelola secara terprogram, server WireMock yang dikelola JUnit dapat dibuat sebagai objek Java dengan nomor port yang diberikan:

@Rule public WireMockRule wireMockRule = new WireMockRule(int port);

Jika tidak ada argumen yang diberikan, port server akan menggunakan nilai default, 8080 . Host server, default ke localhost , dan konfigurasi lainnya dapat ditentukan menggunakan antarmuka Opsi .

4.2. Pencocokan URL

Setelah menyiapkan instance WireMockRule , langkah selanjutnya adalah mengonfigurasi stub. Dalam subbagian ini, kami akan memberikan rintisan REST untuk titik akhir layanan menggunakan ekspresi reguler:

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

Mari beralih ke membuat klien HTTP, menjalankan permintaan dan menerima respons:

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

Potongan kode di atas memanfaatkan metode pembantu konversi:

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

Ini pada gilirannya menggunakan metode pribadi lainnya:

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

The stub's operations are verified by the testing code below:

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 with the matching of 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 of the same helper methods:

CloseableHttpClient httpClient = HttpClients.createDefault(); HttpGet request = new HttpGet("//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

The WireMock library can also be used 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 is 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 define before, convertInputStreamToString.

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

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

HTTP requests and responses can be configured and executed as follows:

CloseableHttpClient httpClient = HttpClients.createDefault(); HttpPost request = new HttpPost("//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 would be 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 are allowed to customize that behavior to take more control of WireMock stubs.

We will demonstrate 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("//localhost:8080/baeldung/wiremock"); request.addHeader("Accept", "text/xml"); return httpClient.execute(request); }

Firstly, 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, create an HTTP client and execute a request using the helper method described right above:

HttpResponse httpResponse = generateClientAndReceiveResponseForPriorityTests();

The following code snippet verifies 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)));

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. Conclusion

This tutorial introduced WireMock and how to set up as well as configure this library for testing of REST APIs using various techniques, including matching of URL, request headers and body.

Penerapan semua contoh dan cuplikan kode dapat ditemukan di proyek GitHub.