Перейти к основному содержимому

Руководство по обеспечению REST

· 8 мин. чтения

Задача: Сумма двух чисел

Напишите функцию twoSum. Которая получает массив целых чисел nums и целую сумму target, а возвращает индексы двух чисел, сумма которых равна target. Любой набор входных данных имеет ровно одно решение, и вы не можете использовать один и тот же элемент дважды. Ответ можно возвращать в любом порядке...

ANDROMEDA

1. Введение

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

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

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

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

Теперь давайте углубимся в простой пример.

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 — http://localhost:8080/events?id=390. :

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

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

Итак, что мы сделали здесь — мы проверили, что вызов конечной точки /events?id=390 отвечает телом, содержащим строку JSON , чей лигаId объекта данных равен 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 :

<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>3.3.0</version>
<scope>test</scope>
</dependency>

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

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

<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>2.1</version>
</dependency>

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

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

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

[1, 2, 3]

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

Мы можем запустить проверку в таком сценарии, используя $ символ или пустую строку («») в качестве пути. Предположим, что мы предоставляем указанную выше службу через http://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-assured для тестирования наших 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));

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

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

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

6. Указание метода запроса

Обычно мы выполняем запрос, вызывая такой метод, как get(), соответствующий методу запроса, который мы хотим использовать.

Кроме того, мы также можем указать глагол HTTP, используя метод request () :

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

Приведенный выше пример эквивалентен прямому использованию get() .

Точно так же мы можем отправлять запросы HEAD , CONNECT и OPTIONS :

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

Запрос POST также следует аналогичному синтаксису, и мы можем указать тело с помощью методов with() и body() .

Поэтому, чтобы создать новый Odd , отправив запрос POST :

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

Нечетный объект , отправленный как тело , будет автоматически преобразован в JSON. Мы также можем передать любую строку , которую хотим отправить, в качестве тела POST . ``

7. Конфигурация значений по умолчанию

Мы можем настроить множество значений по умолчанию для тестов:

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

Здесь мы устанавливаем базовый URI и порт для наших запросов. Помимо этого, мы также можем настроить базовый путь, корневой каталог и аутентификацию.

Примечание. Мы также можем сбросить стандартные значения по умолчанию, гарантированные REST, используя:

RestAssured.reset();

8. Измерьте время отклика

Давайте посмотрим, как мы можем измерить время ответа, используя методы time() и timeIn() объекта Response :

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

assertEquals(timeInS, timeInMS/1000);
}

Обратите внимание, что:

  • time() используется для получения времени ответа в миллисекундах
  • timeIn() используется для получения времени ответа в указанной единице времени.

8.1. Проверить время отклика

Мы также можем проверить время отклика — в миллисекундах — с помощью простого long Matcher:

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

Если мы хотим проверить время отклика в другой единице времени, мы будем использовать сопоставитель time() со вторым параметром TimeUnit :

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

9. Проверка XML-ответа

Он может проверять не только ответ JSON, но и XML.

Предположим, мы делаем запрос к http://localhost:8080/employees и получаем следующий ответ:

<employees>
<employee category="skilled">
<first-name>Jane</first-name>
<last-name>Daisy</last-name>
<sex>f</sex>
</employee>
</employees>

Мы можем проверить, что имяДжейн , вот так:

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

Мы также можем проверить, что все значения соответствуют нашим ожидаемым значениям, объединив сопоставители тела вместе следующим образом:

@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"));
}

Или используя сокращенную версию с переменными аргументами:

@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 для XML

Мы также можем проверить наши ответы с помощью XPath. Рассмотрим приведенный ниже пример, который выполняет сопоставление по имени :

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

XPath также допускает альтернативный способ запуска средства сопоставления equalTo :

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

11. Регистрация сведений о тесте

11.1. Сведения о запросе журнала

Во-первых, давайте посмотрим, как регистрировать всю информацию о запросе с помощью log().all() :

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

Это зарегистрирует что-то вроде этого:

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

Чтобы регистрировать только определенные части запроса, у нас есть метод log() в сочетании с params(), body(), headers(), cookies(), method(), path(), например log.().params( ).

Обратите внимание, что другие используемые библиотеки или фильтры могут изменить то, что фактически отправляется на сервер, поэтому их следует использовать только для регистрации первоначальной спецификации запроса.

11.2. Сведения об ответе журнала

Точно так же мы можем регистрировать детали ответа.

В следующем примере мы регистрируем только тело ответа:

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

Пример вывода:

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

11.3. Ответ в журнале при возникновении условия

У нас также есть возможность протоколировать ответ, только если произошла ошибка или код состояния соответствует заданному значению:

@Test
public void whenLogResponseIfErrorOccurred_thenSuccess() {

when().get("/users/foreach")
.then().log().ifError();
when().get("/users/foreach")
.then().log().ifStatusCodeIsEqualTo(500);
when().get("/users/foreach")
.then().log().ifStatusCodeMatches(greaterThan(200));
}

11.4. Журнал, если проверка не удалась

Мы также можем регистрировать как запрос, так и ответ, только если наша проверка не удалась:

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

given().log().ifValidationFails()
.when().get("/users/foreach")
.then().statusCode(200);
}

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

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

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

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