Регрессионные тесты на REST API

Регрессионные тесты на REST API нужны для того, чтобы проверять, что новые изменения(добавление нового функционала, рефакторинг старого) не ломают старую функциональность. Тесты относительно (UI тестов) быстрые, так как работают на низком уровне по протокоу HTTP и требуют меньше правок из-за редких изменений API, так как для API важна обратная совместимость. Чаще всего они лежат рядом с кодом продукта и запускаются для каждого нового билда. В идеале запуск таких тестов является частью CI и происходит после сборки и установки тестируемого сервиса на тестовый стенд.

Что из себя представляют?

Регрессионные тесты на API в простейшем случае представляют из себя API клиент который делает запросы к тестируемому серверу и валидируют ответ. В более сложных случаях есть подготовка начальных данных для теста (о том как это делается я подробно писал тут), взаимодействие с базой данных, взаимодействие с другими сервисами и многое другое.

Как получить клиент?

Можно написать клиент для тестируемому API самому, при этом поддержка клиента, добавление новых методов, изменение старых будет отнимать время. Можно, при наличие документации, сгенерировать клиент. Подробно о генерации клиента из одной из самых распространенных документации, писал раньше. Здесь можно почитать о плюсах и минусах кодогенерации на примере Rest-assured клиента, сгенеренного на основе документации Swagger.

Как валидировать ответ?

Допустим мы умеем делать запросы к API. Какие подходы есть к валидации ответа?

  • Валидация ответа с заранее заготовленным результатом.

Например (код на java)

assertThat(code, equalTo(200));
assertThat(body, containsString("ERROR"));
assertThat(response.getId(), equalTo(1L));

Тут 200, “ERROR”, 1L - заготовленные данные. Отлично подходит когда ответ API метода небольшой, т.е. для большинства модифицирующих операций.

{
"result": "ok",
"error": ""
}
  • Валидация ответа с автоматически предсказанными данными. В частном случае это сравнение со стабильной версией
assertThat(response, hasNoDiff(expectedResponse));

В данном случае идет сравнение ответов, response полученного с тестируемого сервиса и expectedResponse стабильной (протестированной) версии этого сервиса. Можно провести аналогию с сравнением скриншотам в UI тестах. Подходит когда ответ от API метода большой, т.е. для большинства немодифицирующих операций. Для java и в случае сравнении json, я ранее рассматривал библиотеку jsonUnit

  • Анализ статуса фейкового сервиса. Данный подход очень полезен при тестировании внутренней логики сервиса, но довольно сложен в применении. Например, допустим, нужно проверить, что наш сервис определенным образом взаимодействует с другим сторонним сервисом. Подход заключается в том, что сторонний сервис подменяется заглушкой, логи которого мы можем анализировать в тестах. Также подход может помочь в формировании определенных начальных данных и состояний. Тесты с использованием моков запускаются отдельно от обычных регрессионных тестов, так как требуют изменения тестируемого сервиса.

Пример кода который поднимает мок сервер на локальном порту и валидирует, что на него пришло три определенных запроса. (используется библиотека wiremock)

@Rule
public
WireMockRule wiremock = new WireMockRule(LOCAL_MOCKED_PORT);

@Test
public void shouldSend3Callbacks() throws Exception {
    stubFor(any(urlMatching(".*")).willReturn(aResponse()
            .withStatus(HttpStatus.OK_200).withBody( "OK")));
    // ---------------------------------------------------
    // Здесь та магия, которая инициирует общение сервисов
    // ---------------------------------------------------
    verify(3, postRequestedFor(urlMatching(".*callback.*"))
                    .withRequestBody(matching("^status=.*")));
}