Разделы портала

Онлайн-тренинги

.
Проверка ответов API целиком
04.03.2020 01:00

Автор: Энджи Джонс (Angie Jones)
Оригинал статьи
Перевод: Ольга Алифанова

Большинство людей тестируют API спустя рукава. В своем воркшопе "Азбука API" я прошу людей вручную протестировать API. Получая ответ, они, как правило, бросают на него взгляд и, возможно, тщательно проверяют некоторые ключевые поля. То же верно и для автотестов – обычно проверяются только ключевые поля.

Возьмем вот такой GET-запрос:

http://api.zippopotam.us/us/20500

Он возвращает такой ответ:

HTTP/1.1 200 OK
Date: Sun, 23 Jun 2019 01:51:49 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
Set-Cookie: __cfduid=de53cb3947f3d5b8f887d2ed9514b17ce1561254709; expires=Mon, 22-Jun-20 01:51:49 GMT; path=/; domain=.zippopotam.us; HttpOnly
X-Cache: hit
Charset: UTF-8
Vary: Accept-Encoding
Access-Control-Allow-Origin: *
Server: cloudflare
CF-RAY: 4eb2d1b0ff8e9647-SJC
Content-Encoding: gzip
 
{
"post code": "20500",
"country": "United States",
"country abbreviation": "US",
"places": [
{
"place name": "Washington",
"longitude": "-77.0355",
"state": "District of Columbia",
"state abbreviation": "DC",
"latitude": "38.8946"
}
]
}

Получая такой ответ, многие юнит-тесты убедятся, что код ответа 200, и тело не пустое. Функциональные тесты могут пойти дальше и проверить некоторые ключевые поля тела ответа.

Большая часть людей не будет проектировать тесты, проверяющие КАЖДЫЙ кусочек ответа, потому что это очень времязатратно, и с шансами неверная информация в некоторых полях несет не такие уж большие риски.

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

Из-за этого я искала способ проверить ответ целиком, используя только одну проверку. Я была близка к этому, десериализируя ответ API в POJO и сравнивая результат с ожидаемым объектом. Но даже при этом подходе мне нужно было кодировать POJO, и создавать ожидаемый объект в моем коде. Все равно времязатратно.

К счастью, Марк Уинтерингэм сделал воркшоп по Approval Tests и показал, как проверить тело ответа целиком! Я пришла домой, попробовала сделать это самостоятельно, и мне это нравится!

После первого прогона вашего теста Approval Tests сохранит ваш ожидаемый результат в файл. Затем для каждого последующего прогона он сравнит новый результат с сохраненным. Если результат отличается, тест падает, и дифф-программа вроде DiffMerge может показать вам разницу между файлами.

Видео: https://video.twimg.com/tweet_video/DZ4gjREXkAEeQqI.mp4

Зависимости

Я создала pom-файл при помощи Approval Tests, TestNG (как прогонщика тестов), и Rest-Assured (для API). Отметьте, что Approval Tests может работать с любым прогонщиком тестов и любым API-инструментом, и поддерживает несколько языков разработки.

<dependencies>
<dependency>
<groupId>com.approvaltests</groupId>
<artifactId>approvaltests</artifactId>
<version>RELEASE</version>
</dependency>
 
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.14.3</version>
</dependency>
 
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>4.0.0</version>
</dependency>
</dependencies>

Тестирование тела ответа

В этом тесте я хочу проверить тело ответа целиком. Я могу сделать это, вызывая Approvals.verify и передавая тело. Это проверяет тело, однако код статуса все еще может быть неверным. Поэтому я добавляю еще один вызов к методу Rest-Assured statusCode, чтобы проверить и статус тоже.

@Test
public void testWhiteHouseZipBody(){
Response response = given().get("http://api.zippopotam.us/us/20500");
Approvals.verify(response.body().prettyPrint());
response.then().statusCode(200);
}

При первом выполнении тест упадет, потому что файл approved еще не сохранен.

 

Я беру результат, убеждаюсь, что он верен, и сохраняю его в файл approved.

При повторном прогоне Approval Test сравнивает файл received, содержащий новую информацию, с файлом approved. Если они совпадают, тест пройден.

Тестирование ответа целиком

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

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

@Test
public void testWhiteHouseZipAll(){
Response response = given().get("http://api.zippopotam.us/us/20500");
String responseStr = response.headers().toString() + "\n\n" + response.body().prettyPrint();
Approvals.verify(responseStr);
}

Динамические данные

Используя эту технику, вы быстро столкнетесь с тем, что большая часть ответов динамична. Для вызовов POST ответ может включать заново сгенерированный ID. Заголовки могут включать дату или информацию о cookie, которая каждый раз разная. К счастью, я вспомнила, что Марк научил нас небольшому фокусу, чтобы справиться с этим.

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

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

@Test
public void testWhiteHouseZipAll(){
Response response = given().get("http://api.zippopotam.us/us/20500");
String responseStr = response.headers().toString() + "\n\n" + response.body().prettyPrint();
 
responseStr = responseStr.replaceAll("Date=.*", "#####DATE");
responseStr = responseStr.replaceAll("Set-Cookie=.*", "#####COOKIE");
responseStr = responseStr.replaceAll("CF-RAY=.*", "#####CF-RAY");
 
Approvals.verify(responseStr);
}
Содержимое файла затем сохраняется так:
#####DATE
Content-Type=application/json
Transfer-Encoding=chunked
Connection=keep-alive
#####COOKIE
X-Cache=hit
Charset=UTF-8
Vary=Accept-Encoding
Access-Control-Allow-Origin=*
Server=cloudflare
#####CF-RAY
Content-Encoding=gzip
 
{
"post code": "20500",
"country": "United States",
"country abbreviation": "US",
"places": [
{
"place name": "Washington",
"longitude": "-77.0355",
"state": "District of Columbia",
"state abbreviation": "DC",
"latitude": "38.8946"
}
]
}

Скачать код

Я почистила код и разместила его на GitHub для вашего удобства.

Обсудить в форуме