Руководство по REST-гарантированному

Джексон Топ

Я только что анонсировал новый курс Learn Spring , посвященный основам Spring 5 и Spring Boot 2:

>> ПРОВЕРИТЬ КУРС

1. Введение

REST-assured был разработан для упрощения тестирования и проверки REST API и сильно зависит от методов тестирования, используемых в динамических языках, таких как Ruby и Groovy.

Библиотека имеет надежную поддержку HTTP, начиная, конечно, с глаголов и стандартных операций HTTP, но также выходит далеко за рамки этих основ.

В этом руководстве мы собираемся исследовать REST-гарантированный, и мы собираемся использовать Hamcrest для выполнения утверждений. Если вы еще не знакомы с Hamcrest, вам следует сначала освежить в памяти учебник: Тестирование с Hamcrest.

Кроме того, чтобы узнать о более сложных случаях использования REST-гарантированного, ознакомьтесь с другими нашими статьями:

  • Гарантия REST с Groovy
  • Проверка схемы JSON с гарантией REST
  • Параметры, заголовки и файлы cookie с гарантией REST

Теперь давайте рассмотрим простой пример.

2. Простой пример теста

Прежде чем мы начнем, давайте удостоверимся, что наши тесты имеют следующие статические импорта:

io.restassured.RestAssured.* io.restassured.matcher.RestAssuredMatchers.* org.hamcrest.Matchers.*

Нам это понадобится, чтобы тесты оставались простыми и имели легкий доступ к основным API.

Теперь давайте начнем с простого примера - базовой системы ставок, предоставляющей некоторые данные для игр:

{ "id": "390", "data": { "leagueId": 35, "homeTeam": "Norway", "visitingTeam": "England", }, "odds": [{ "price": "1.30", "name": "1" }, { "price": "5.25", "name": "X" }] }

Скажем, это ответ JSON на обращение к локально развернутому API - // localhost: 8080 / events? Id = 390. :

Теперь давайте воспользуемся REST-гарантией, чтобы проверить некоторые интересные особенности ответа JSON:

@Test public void givenUrl_whenSuccessOnGetsResponseAndJsonHasRequiredKV_thenCorrect() { get("/events?id=390").then().statusCode(200).assertThat() .body("data.leagueId", equalTo(35)); }

Итак, что мы сделали здесь - мы убедились , что вызов конечных точек / события ID = 390? Откликаются с телом , содержащим JSON строки которого leagueId из данных объекта 35.

Давайте посмотрим на более интересный пример. Допустим, вы хотите убедиться, что в массиве коэффициентов есть записи с ценами 1,30 и 5,25 :

@Test public void givenUrl_whenJsonResponseHasArrayWithGivenValuesUnderKey_thenCorrect() { get("/events?id=390").then().assertThat() .body("odds.price", hasItems("1.30", "5.25")); }

3. Настройка с поддержкой REST.

Если ваш любимый инструмент зависимостей - Maven, мы добавляем следующую зависимость в файл pom.xml :

 io.rest-assured rest-assured 3.3.0 test 

Чтобы получить последнюю версию, перейдите по этой ссылке.

REST-assured использует возможности сопоставителей Hamcrest для выполнения своих утверждений, поэтому мы также должны включить эту зависимость:

 org.hamcrest hamcrest-all 2.1 

Последняя версия всегда будет доступна по этой ссылке.

4. Анонимная проверка корня JSON

Рассмотрим массив, состоящий из примитивов, а не объектов:

[1, 2, 3]

Это называется анонимным корнем JSON, что означает, что у него нет пары "ключ-значение", тем не менее, это все еще действительные данные JSON.

Мы можем запустить проверку в таком сценарии, используя $символ или пустую строку («») в качестве пути. Предположим, мы предоставляем вышеуказанный сервис через // localhost: 8080 / json, тогда мы можем проверить его следующим образом с помощью REST-assured:

when().get("/json").then().body("$", hasItems(1, 2, 3));

или вот так:

when().get("/json").then().body("", hasItems(1, 2, 3));

5. Поплавки и парные удары

Когда мы начинаем использовать REST-гарантированный для тестирования наших REST-сервисов, нам нужно понимать, что числа с плавающей запятой в ответах JSON отображаются на примитивный тип float.

Использование типа float не взаимозаменяемо с double, как это имеет место во многих сценариях в java.

Речь идет о следующем ответе:

{ "odd": { "price": "1.30", "ck": 12.2, "name": "1" } }

Предположим, мы проводим следующий тест для значения ck :

get("/odd").then().assertThat().body("odd.ck", equalTo(12.2));

Этот тест завершится неудачно, даже если значение, которое мы тестируем, равно значению в ответе. Это потому, что мы сравниваем с двойным, а не с плавающим .

To make it work, we have to explicitly specify the operand to the equalTo matcher method as a float, like so:

get("/odd").then().assertThat().body("odd.ck", equalTo(12.2f));

6. Specifying the Request Method

Typically, we would perform a request by calling a method such as get(), corresponding to the request method we want to use.

In addition, we can also specify the HTTP verb using the request() method:

@Test public void whenRequestGet_thenOK(){ when().request("GET", "/users/eugenp").then().statusCode(200); }

The example above is equivalent to using get() directly.

Similarly, we can send HEAD, CONNECT and OPTIONS requests:

@Test public void whenRequestHead_thenOK() { when().request("HEAD", "/users/eugenp").then().statusCode(200); }

POST request also follows a similar syntax and we can specify the body by using the with() and body() methods.

Therefore, to create a new Odd by sending a POST request:

@Test public void whenRequestedPost_thenCreated() { with().body(new Odd(5.25f, 1, 13.1f, "X")) .when() .request("POST", "/odds/new") .then() .statusCode(201); }

The Odd object sent as body will automatically be converted to JSON. We can also pass any String that we want to send as our POSTbody.

7. Default Values Configuration

We can configure a lot of default values for the tests:

@Before public void setup() { RestAssured.baseURI = "//api.github.com"; RestAssured.port = 443; }

Here, we're setting a base URI and port for our requests. Besides these, we can also configure the base path, root pat, and authentication.

Note: we can also reset to the standard REST-assured defaults by using:

RestAssured.reset();

8. Measure Response Time

Let's see how we can measure the response time using the time() and timeIn() methods of the Response object:

@Test public void whenMeasureResponseTime_thenOK() { Response response = RestAssured.get("/users/eugenp"); long timeInMS = response.time(); long timeInS = response.timeIn(TimeUnit.SECONDS); assertEquals(timeInS, timeInMS/1000); }

Note that:

  • time() is used to get response time in milliseconds
  • timeIn() is used to get response time in the specified time unit

8.1. Validate Response Time

We can also validate the response time – in milliseconds – with the help of simple longMatcher:

@Test public void whenValidateResponseTime_thenSuccess() { when().get("/users/eugenp").then().time(lessThan(5000L)); }

If we want to validate the response time in a different time unit, then we'll use the time() matcher with a second TimeUnit parameter:

@Test public void whenValidateResponseTimeInSeconds_thenSuccess(){ when().get("/users/eugenp").then().time(lessThan(5L),TimeUnit.SECONDS); }

9. XML Response Verification

Not only can it validate a JSON response, itcan validate XML as well.

Let's assume we make a request to //localhost:8080/employees and we get the following response:

  Jane Daisy f  

We can verify that the first-name is Jane like so:

@Test public void givenUrl_whenXmlResponseValueTestsEqual_thenCorrect() { post("/employees").then().assertThat() .body("employees.employee.first-name", equalTo("Jane")); }

We can also verify that all values match our expected values by chaining body matchers together like so:

@Test public void givenUrl_whenMultipleXmlValuesTestEqual_thenCorrect() { post("/employees").then().assertThat() .body("employees.employee.first-name", equalTo("Jane")) .body("employees.employee.last-name", equalTo("Daisy")) .body("employees.employee.sex", equalTo("f")); }

Or using the shorthand version with variable arguments:

@Test public void givenUrl_whenMultipleXmlValuesTestEqualInShortHand_thenCorrect() { post("/employees") .then().assertThat().body("employees.employee.first-name", equalTo("Jane"),"employees.employee.last-name", equalTo("Daisy"), "employees.employee.sex", equalTo("f")); }

10. XPath for XML

We can also verify our responses using XPath. Consider the example below that executes a matcher on the first-name:

@Test public void givenUrl_whenValidatesXmlUsingXpath_thenCorrect() { post("/employees").then().assertThat(). body(hasXPath("/employees/employee/first-name", containsString("Ja"))); }

XPath also accepts an alternate way of running the equalTo matcher:

@Test public void givenUrl_whenValidatesXmlUsingXpath2_thenCorrect() { post("/employees").then().assertThat() .body(hasXPath("/employees/employee/first-name[text()='Jane']")); }

11. Logging Test Details

11.1. Log Request Details

First, let's see how to log entire request details using log().all():

@Test public void whenLogRequest_thenOK() { given().log().all() .when().get("/users/eugenp") .then().statusCode(200); }

This will log something like this:

Request method: GET Request URI: //api.github.com:443/users/eugenp Proxy:  Request params:  Query params:  Form params:  Path params:  Multiparts:  Headers: Accept=*/* Cookies:  Body: 

To log only specific parts of the request, we have the log() method in combination with params(), body(), headers(), cookies(), method(), path() eg log.().params().

Note that other libraries or filters used may alter what's actually sent to the server, so this should only be used to log the initial request specification.

11.2. Log Response Details

Similarly, we can log the response details.

In the following example we're logging the response body only:

@Test public void whenLogResponse_thenOK() { when().get("/repos/eugenp/tutorials") .then().log().body().statusCode(200); }

Sample output:

{ "id": 9754983, "name": "tutorials", "full_name": "eugenp/tutorials", "private": false, "html_url": "//github.com/eugenp/tutorials", "description": "The \"REST With Spring\" Course: ", "fork": false, "size": 72371, "license": { "key": "mit", "name": "MIT License", "spdx_id": "MIT", "url": "//api.github.com/licenses/mit" }, ... }

11.3. Log Response if Condition Occurred

We also have the option of logging the response only if an error occurred or the status code matches a given value:

@Test public void whenLogResponseIfErrorOccurred_thenSuccess() { when().get("/users/eugenp") .then().log().ifError(); when().get("/users/eugenp") .then().log().ifStatusCodeIsEqualTo(500); when().get("/users/eugenp") .then().log().ifStatusCodeMatches(greaterThan(200)); }

11.4. Log if Validation Failed

We can also log both request and response only if our validation failed:

@Test public void whenLogOnlyIfValidationFailed_thenSuccess() { when().get("/users/eugenp") .then().log().ifValidationFails().statusCode(200); given().log().ifValidationFails() .when().get("/users/eugenp") .then().statusCode(200); }

В этом примере мы хотим подтвердить, что код состояния равен 200. Только в случае неудачи запрос и ответ будут зарегистрированы.

12. Заключение

В этом руководстве мы исследовали среду с гарантированным REST и рассмотрели ее наиболее важные функции, которые мы можем использовать для тестирования наших служб RESTful и проверки их ответов.

Полную реализацию всех этих примеров и фрагментов кода можно найти в проекте GitHub с поддержкой REST.

Джексон дно

Я только что анонсировал новый курс Learn Spring , посвященный основам Spring 5 и Spring Boot 2:

>> ПРОВЕРИТЬ КУРС