Руководство по гарантированному отдыху

Руководство по гарантированному отдыху

1. Вступление

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

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

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

Кроме того, чтобы узнать о более продвинутых сценариях использования 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 -http://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));
}

Итак, что мы здесь сделали - мы проверили, что вызов конечной точки/events?id=390 отвечает телом, содержащимJSON String, чейleagueId объектаdata равен 35.

Давайте посмотрим на более интересный пример. Допустим, вы хотите убедиться, что в массивеodds есть записи с ценами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.0.0
    test

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


    org.hamcrest
    hamcrest-all
    1.3

Последняя версия всегда будет доступна по адресуthis link.

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-гарантированный для тестирования наших 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));

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

Чтобы это работало, мы должны явно указать операнд для метода сопоставленияequalTo какfloat, например:

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

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

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

Кроме того,we can also specify the HTTP verb using the request() method:

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

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

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

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

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

Следовательно, чтобы создать новыйOdd , отправив srequestPOST :

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

ОбъектOdd , отправленный какbody will, автоматически будет преобразован в JSON. Мы также можем передать любойString, который мы хотим отправить, как нашPOSTbody.

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

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

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

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

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

RestAssured.reset();

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

Давайте посмотрим, как мы можем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);
}

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

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

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

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

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

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

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

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

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

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

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


    
        Jane
        Daisy
        f
    

Мы можем проверить, чтоfirst-name являетсяJane следующим образом:

@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

We can also verify our responses using XPath. Рассмотрим приведенный ниже пример, в котором выполняется сопоставлениеfirst-name:

@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 entire request details using*log().all()*:

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

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

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

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

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

11.2. Журнал подробностей ответа

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

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

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

Образец вывода:

{
    "id": 9754983,
    "name": "tutorials",
    "full_name": "eugenp/tutorials",
    "private": false,
    "html_url": "https://github.com/eugenp/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/eugenp")
      .then().log().ifError();
    when().get("/users/eugenp")
      .then().log().ifStatusCodeIsEqualTo(500);
    when().get("/users/eugenp")
      .then().log().ifStatusCodeMatches(greaterThan(200));
}

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

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

@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. Заключение

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

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