API играет важную роль в развитии ПО. Поэтому правильное автоматизированное тестирование API становится всё более необходимым. Существует множество различных инструментов, которые помогут вам в написании автоматизированных тестов. Эта статья расскажет об использовании одного из самых популярных инструментов для этой задачи: REST Assured.
REST Assured — это библиотека Java, предназначенная для тестирования RESTful API. Вы также можете копировать и использовать примеры кода из этой статьи.
Подпишитесь на наш ТЕЛЕГРАМ КАНАЛ ПО АВТОМАТИЗАЦИИ ТЕСТИРОВАНИЯ
Начало работы: Конфигурация
Чтобы начать работу с REST Assured, просто внесите его в качестве зависимости в свой проект. Если вы используете Maven, добавьте следующую запись в ваш pom.xml
:
<dependency> <groupId>io.rest-assured</groupId> <artifactId>rest-assured</artifactId> <version>3.0.2</version> <scope>test</scope> </dependency>
И для Gradle:
testCompile 'io.rest-assured:rest-assured:3.0.2'
REST Assured можно использовать с фреймворками для модульного тестирования, такими как JUnit и TestNG. В примерах из этой статьи REST Assured использовался вместе с TestNG.
После настройки импорта REST Assured, добавьте следующие статические «импорты» в ваш тестовый класс:
import static io.restassured.RestAssured.*; import static org.hamcrest.Matchers.*;
Первый тест: Понимание синтаксиса
В статье будет тестироваться API базы данных автогонок Ergast, который можно найти здесь. Этот API предоставляет данные, связанные с гонками Формулы-1, гонщиками, трассами и многим другим.
Ниже приведён тест, написанный с помощью REST Assured, который получает список трасс 2017 года в формате JSON и проверяет, есть ли в списке 20 трасс:
@Test public void test_NumberOfCircuitsFor2017Season_ShouldBe20() { given(). when(). get("http://ergast.com/api/f1/2017/circuits.json"). then(). assertThat(). body("MRData.CircuitTable.Circuits.circuitId",hasSize(20)); }
Обратите внимание, что API, используемый REST Assured, поддерживает синтаксис Given/When/Then. Благодаря чему тест легко читается и выполняет все необходимые действия всего в одной строке кода.
Матчер hasSize()
в Hamcrest подсчитывает количество трасс — вот почему вам нужно было добавить Hamcrest в качестве статического импорта. Библиотека Hamcrest содержит набор матчеров, которые позволяют создавать верификации всех видов, сохраняя их читабельность.
В верификационной части теста выполняется следующее:
- Запоминает (JSON) ответ на запрос API.
- Запрашивает все элементы с именем
circuitId
, используя выражениеGroovy GPath "MRData.CircuitTable.Circuits.circuitId"
. - Проверяет, что полученное количество элементов
circuitId
равно 20.
В Hamcrest существуют матчеры для большого количества различных проверок, включая equalTo()
для равенства, lessThan()
и greaterThan()
для сравнения, hasItem()
для проверки, содержит ли база данных запрашиваемый элемент, и многие другие. Полный список матчеров приведен в документации библиотеки Hamcrest.
Валидация данных ответа
С помощью REST Assured вы можете не только проверить содержимое тела ответа, но и проконтролировать его технические характеристики, такие как код состояния HTTP, формат содержимого ответа и т. д. В приведенном ниже примере проверяется следующее:
- Код состояния ответа — 200.
- Формат содержимого ответа (указывает получателю, как интерпретировать тело ответа) —
"application/json"
. - Значение заголовка ответа
"Content-Length"
равно 4567.
@Test public void test_ResponseHeaderData_ShouldBeCorrect() { given(). when(). get("http://ergast.com/api/f1/2017/circuits.json"). then(). assertThat(). statusCode(200). and(). contentType(ContentType.JSON). and(). header("Content-Length",equalTo("4567")); }
Этот пример также показывает, как легко объединить проверки в удобный и читаемый вид с помощью метода and()
. Единственная функция этого метода — делать код более простым к восприятию.
Параметризованные тесты
Часто возникает необходимость проводить один и тот же тест с различными наборами данных. Вместо того чтобы писать новый тест для каждого набора, вы можете создать параметризованный тест (parameterized test). И передать ему столько тестовых данных, сколько требуется для успешного покрытия теста.
RESTful API поддерживают два различных типа параметров:
- Параметры запроса (query parameters). Они добавляются в конце конечной точки RESTful API и их можно отличить по знаку вопроса перед ними. Например, в конечной точке
http://md5.jsontest.com/?text=test
“text” является параметром запроса (со значением “test”). - Параметры пути (path parameters). Они являются частью конечной точки RESTful API. Например, в конечной точке
http://ergast.com/api/f1/2017/circuits.json
, “2017” — это значение параметра пути. Попробуйте заменить его на “2016” и посмотрите, что произойдет (подсказка: тест должен провалиться, так как в 2016 году была 21 гонка Формулы-1, а не 20).
REST Assured может работать с обоими типами параметров. Сначала посмотрим, как можно задать и использовать параметр запроса из примера выше:
@Test public void test_Md5CheckSumForTest_ShouldBe098f6bcd4621d373cade4e832627b4f6() { String originalText = "test"; String expectedMd5CheckSum = "098f6bcd4621d373cade4e832627b4f6"; given(). param("text",originalText). when(). get("http://md5.jsontest.com"). then(). assertThat(). body("md5",equalTo(expectedMd5CheckSum)); }
Как видите, использовать параметры запроса в REST Assured очень просто, для этого нужно указать их имя и значение с помощью метода param()
. Параметры пути задаются аналогичным образом:
@Test public void test_NumberOfCircuits_ShouldBe20_Parameterized() { String season = "2017"; int numberOfRaces = 20; given(). pathParam("raceSeason",season). when(). get("http://ergast.com/api/f1/{raceSeason}/circuits.json"). then(). assertThat(). body("MRData.CircuitTable.Circuits.circuitId",hasSize(numberOfRaces)); }
Вместо param()
параметры пути определяются с помощью метода pathParam()
. Кроме того, вам необходимо определить, какая часть конечной точки представляет собой переменную path, что делается с помощью фигурных скобок. Таким же образом можно создать более одного параметра пути и даже объединить эти параметры в одном запросе.
Теперь, когда мы параметризовали тест, наполнить его набором тестовых данных — несложная задача. В TestNG достаточно создать объект DataProvider
, содержащий необходимые тестовые данные, в нашем случае — это набор данных, содержащий годы и количество гонок Формулы-1 в каждом из них:
@DataProvider(name="seasonsAndNumberOfRaces") public Object[][] createTestDataRecords() { return new Object[][] { {"2017",20}, {"2016",21}, {"1966",9} }; }
Затем просто передаем тестовые данные параметризованному тесту с помощью основных методов REST Assured:
@Test(dataProvider="seasonsAndNumberOfRaces") public void test_NumberOfCircuits_ShouldBe_DataDriven(String season, int numberOfRaces) { given(). pathParam("raceSeason",season). when(). get("http://ergast.com/api/f1/{raceSeason}/circuits.json"). then(). assertThat(). body("MRData.CircuitTable.Circuits.circuitId",hasSize(numberOfRaces)); }
Теперь вы можете добавлять, удалять или обновлять отдельные тест-кейсы, просто создавая или изменяя соответствующую запись.
Доступ к защищенным API
Часто API защищены с помощью механизма аутентификации. REST Assured поддерживает базовую, дайджест и OAuth аутентификацию. Вот пример вызова RESTful API, защищённого с помощью базовой аутентификации (т. е. пользователь этого API должен предоставлять правильное имя и пароль при каждом вызове API):
@Test public void test_APIWithBasicAuthentication_ShouldBeGivenAccess() { given(). auth(). preemptive(). basic("username", "password"). when(). get("http://path.to/basic/secured/api"). then(). assertThat(). statusCode(200); }
Получить доступ к API, защищенному OAuth2, просто, если у вас есть действительный токен аутентификации:
@Test public void test_APIWithOAuth2Authentication_ShouldBeGivenAccess() { given(). auth(). oauth2(YOUR_AUTHENTICATION_TOKEN_GOES_HERE). when(). get("http://path.to/oath2/secured/api"). then(). assertThat(). statusCode(200); }
Передача значений между тестами
Часто при тестировании RESTful API возникает необходимость в создании более сложных тест-кейсов. В них требуется получить значение из ответа одного вызова API и повторно использовать его в следующем вызове. Это можно сделать с помощью метода extract()
. В качестве примера приведён сценарий, который извлекает идентификатор первой трассы сезона 2017 года и использует его для получения и проверки дополнительной информации об этой трассе (в данном случае трасса находится в Австралии):
@Test public void test_ScenarioRetrieveFirstCircuitFor2017SeasonAndGetCountry_ShouldBeAustralia() { // First, retrieve the circuit ID for the first circuit of the 2017 season String circuitId = given(). when(). get("http://ergast.com/api/f1/2017/circuits.json"). then(). extract(). path("MRData.CircuitTable.Circuits.circuitId[0]"); // Then, retrieve the information known for that circuit and verify it is located in Australia given(). pathParam("circuitId",circuitId). when(). get("http://ergast.com/api/f1/circuits/{circuitId}.json"). then(). assertThat(). body("MRData.CircuitTable.Circuits.Location[0].country",equalTo("Australia")); }
Повторное использование проверок с помощью ResponseSpecBuilder
Ещё один способ улучшить многократное использование ваших тестов RESTful API — это повторное использование определённых проверок. Например, если вы хотите удостовериться, что все получаемые ответы имеют код состояния, равный 200, а их формат соответствует "application/json"
. Проверка этого для каждого теста может быть очень утомительна.
REST Assured поддерживает повторное использование определенных проверок с помощью механизма ResponseSpecBuilder
.
Вот пример того, как создать повторно используемую ResponseSpecification
, проверяющую код состояния и формат содержимого ответов:
ResponseSpecification checkStatusCodeAndContentType = new ResponseSpecBuilder(). expectStatusCode(200). expectContentType(ContentType.JSON). build();
Test public void test_NumberOfCircuits_ShouldBe20_UsingResponseSpec() { given(). when(). get("http://ergast.com/api/f1/2017/circuits.json"). then(). assertThat(). spec(checkStatusCodeAndContentType). and(). body("MRData.CircuitTable.Circuits.circuitId",hasSize(20)); }
Другие возможности REST Assured
Помимо уже описанных, REST Assured предлагает ряд других полезных функций:
- Возможность (де-) сериализации Java-объектов (POJO). Это позволяет вам перевести свойства и значения Java-объекта, в JSON или XML документ и наоборот. Затем этот документ можно отправить с помощью метода POST.
- Сохранение запросов и ответов. Это полезно, если нужно просмотреть ответы API для создания соответствующих проверок, или убедиться, что отправленный запрос корректен.
- REST Assured поставляется с модулем Spring Mock MVC, позволяющим писать тесты для контроллеров Spring, используя синтаксис REST Assured.
Перевод статьи Bas Dijkstra «How to perform API testing with REST Assured».