Въведение в WireMock

1. Общ преглед

WireMock е библиотека за смазване и подигравка на уеб услуги. Той създава HTTP сървър, към който бихме могли да се свържем, както бихме направили с действителна уеб услуга.

Когато сървърът WireMock е в действие, можем да зададем очаквания, да се обадим на услугата и след това да проверим поведението му.

2. Зависимости на Maven

За да можем да се възползваме от библиотеката WireMock, трябва да включим следната зависимост в POM:

 com.github.tomakehurst wiremock 1.58 test 

3. Програмно управляван сървър

Този раздел ще обхване начина за ръчно конфигуриране на сървър WireMock. т.е. без поддръжката на JUnit автоматично конфигуриране. Използването е демонстрирано от много проста мъничка.

3.1. Настройка на сървъра

Сървър WireMock може да бъде създаден по този начин:

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

В случай че не са предоставени аргументи, хостът на сървъра по подразбиране е localhost, а портът на сървъра - 8080 .

След това сървърът може да бъде стартиран и спрян чрез два прости метода:

wireMockServer.start();

И:

wireMockServer.stop();

3.2. Основна употреба

Библиотеката WireMock първо ще бъде демонстрирана чрез основно използване, където е осигурен заглушител за точен URL адрес без допълнителна конфигурация. Нека създадем екземпляр на сървър:

WireMockServer wireMockServer = new WireMockServer();

Сървърът WireMock трябва да работи преди клиентът да се свърже с него:

wireMockServer.start();

След това уеб услугата се блокира:

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

Този урок използва Apache HttpClient API за представяне на клиент, свързващ се към сървъра:

CloseableHttpClient httpClient = HttpClients.createDefault();

Заявка се изпълнява и отговор се връща, съответно, след това:

HttpGet request = new HttpGet("//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 управляван сървър

За разлика от раздел 3, този раздел илюстрира използването на WireMock сървър с помощта на JUnit Rule .

4.1. Настройка на сървъра

Сървър WireMock може да бъде интегриран в тестовите случаи на JUnit с помощта на анотацията @Rule . Това позволява на JUnit да управлява жизнения цикъл, като стартира сървъра преди всеки тестов метод и го спира след връщането на метода.

Подобно на програмно управлявания сървър, управляван от JUnit сървър WireMock може да бъде създаден като Java обект с даден номер на порт:

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

Ако не са предоставени аргументи, сървърният порт ще приеме стойността по подразбиране, 8080 . Хостът на сървъра, настройката по подразбиране за localhost и други конфигурации могат да бъдат посочени с помощта на интерфейса Options .

4.2. URL съвпадение

След настройване на екземпляр WireMockRule , следващата стъпка е да конфигурирате мъниче. В този подраздел ще предоставим REST мъниче за крайна точка на услугата, използвайки регулярен израз:

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

Нека да преминем към създаване на HTTP клиент, изпълнение на заявка и получаване на отговор:

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

Горният кодов фрагмент се възползва от помощния метод за преобразуване:

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

Това от своя страна използва друг частен метод:

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.

Прилагането на всички примери и кодови фрагменти може да бъде намерено в проект на GitHub.