Что такое REST Assured?
REST Assured — это Java-библиотека, упрощающая тестирование RESTful API. Она предоставляет предметно-ориентированный язык (DSL) на основе паттерна Given-When-Then, который делает API-тесты читаемыми как спецификации на естественном языке. Вместо ручного построения HTTP-запросов, парсинга ответов и написания сложных проверок, REST Assured обрабатывает эти операции через fluent, цепочечный API.
REST Assured — самая популярная библиотека API-тестирования в экосистеме Java, используемая тысячами организаций для автоматизированной валидации API. Она интегрируется с JUnit, TestNG, Maven, Gradle и CI/CD-пайплайнами.
Настройка проекта
Конфигурация Maven
<!-- pom.xml -->
<dependencies>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>5.4.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>json-schema-validator</artifactId>
<version>5.4.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
</dependencies>
Паттерн Given-When-Then
Каждый тест REST Assured следует трём фазам:
given() // Настройка запроса: заголовки, тело, параметры, авторизация
.header("Content-Type", "application/json")
.body(requestBody)
.when() // Выполнение HTTP-метода
.post("/api/users")
.then() // Валидация ответа
.statusCode(201)
.body("name", equalTo("Alice"));
Операции CRUD
GET-запрос
@Test
void shouldReturnAllUsers() {
given()
.baseUri("https://api.example.com")
.when()
.get("/api/users")
.then()
.statusCode(200)
.body("size()", greaterThan(0))
.body("[0].name", notNullValue());
}
@Test
void shouldReturnUserById() {
given()
.baseUri("https://api.example.com")
.pathParam("id", 42)
.when()
.get("/api/users/{id}")
.then()
.statusCode(200)
.body("id", equalTo(42))
.body("name", equalTo("Alice"));
}
POST-запрос
@Test
void shouldCreateNewUser() {
String requestBody = """
{
"name": "Bob Johnson",
"email": "bob@example.com",
"role": "developer"
}
""";
given()
.baseUri("https://api.example.com")
.contentType(ContentType.JSON)
.body(requestBody)
.when()
.post("/api/users")
.then()
.statusCode(201)
.body("id", notNullValue())
.body("name", equalTo("Bob Johnson"));
}
PUT и DELETE
@Test
void shouldUpdateUser() {
String updateBody = """
{ "name": "Alice Updated", "role": "lead" }
""";
given()
.contentType(ContentType.JSON)
.body(updateBody)
.when()
.put("/api/users/42")
.then()
.statusCode(200)
.body("name", equalTo("Alice Updated"));
}
@Test
void shouldDeleteUser() {
when()
.delete("/api/users/42")
.then()
.statusCode(204);
}
Параметры запроса и заголовки
@Test
void shouldFilterUsersWithQueryParams() {
given()
.queryParam("role", "admin")
.queryParam("active", true)
.queryParam("page", 1)
.when()
.get("/api/users")
.then()
.statusCode(200)
.body("every { it.role == 'admin' }", is(true));
}
Извлечение ответа
@Test
void shouldExtractAndUseResponseData() {
int userId = given()
.contentType(ContentType.JSON)
.body("{ \"name\": \"Test User\", \"email\": \"test@example.com\" }")
.when()
.post("/api/users")
.then()
.statusCode(201)
.extract()
.path("id");
given()
.pathParam("id", userId)
.when()
.get("/api/users/{id}")
.then()
.statusCode(200)
.body("name", equalTo("Test User"));
}
Аутентификация
// Basic Auth
given()
.auth().basic("username", "password")
.when()
.get("/api/protected");
// Bearer Token
given()
.header("Authorization", "Bearer " + token)
.when()
.get("/api/protected");
// OAuth 2.0
given()
.auth().oauth2(accessToken)
.when()
.get("/api/protected");
Валидация JSON-схемы
@Test
void shouldMatchUserSchema() {
given()
.when()
.get("/api/users/1")
.then()
.statusCode(200)
.body(matchesJsonSchemaInClasspath("schemas/user-schema.json"));
}
Переиспользуемые спецификации
public class ApiSpecs {
public static RequestSpecification baseSpec() {
return new RequestSpecBuilder()
.setBaseUri("https://api.example.com")
.setContentType(ContentType.JSON)
.addHeader("X-API-Version", "2")
.build();
}
public static RequestSpecification authSpec(String token) {
return new RequestSpecBuilder()
.addRequestSpecification(baseSpec())
.addHeader("Authorization", "Bearer " + token)
.build();
}
}
// Использование в тестах
@Test
void shouldGetUserWithSpecs() {
given()
.spec(ApiSpecs.authSpec(token))
.when()
.get("/api/users/1")
.then()
.statusCode(200)
.body("name", notNullValue());
}
Логирование для отладки
given()
.log().all() // Полный лог запроса
.when()
.get("/api/users")
.then()
.log().ifError() // Лог ответа только при ошибке
.statusCode(200);
Упражнения
Упражнение 1: CRUD-сюита тестов
Постройте полную CRUD-сюиту тестов для REST API:
- POST — Создать новый ресурс, извлечь его ID
- GET — Получить ресурс по ID, валидировать все поля
- PUT — Обновить конкретные поля, проверить изменения
- DELETE — Удалить ресурс, проверить 404 при последующем GET
- Использовать переиспользуемую RequestSpecification для base URL и авторизации
Упражнение 2: Валидация схемы
- Создайте файл JSON-схемы для объекта ответа пользователя
- Напишите тест, валидирующий схему для эндпоинтов отдельного пользователя и списка
- Модифицируйте схему, сделав поле обязательным, и проверьте, что тест обнаружит нарушение
- Добавьте валидацию схемы к CRUD-сюите из Упражнения 1
Упражнение 3: Поток аутентификации
- Напишите тест аутентификации через POST /auth/login с извлечением JWT-токена
- Используйте извлечённый токен для последующих авторизованных запросов
- Протестируйте, что неавторизованные запросы возвращают 401
- Протестируйте, что просроченные токены возвращают 403 с подходящим сообщением об ошибке