Что такое 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:

  1. POST — Создать новый ресурс, извлечь его ID
  2. GET — Получить ресурс по ID, валидировать все поля
  3. PUT — Обновить конкретные поля, проверить изменения
  4. DELETE — Удалить ресурс, проверить 404 при последующем GET
  5. Использовать переиспользуемую RequestSpecification для base URL и авторизации

Упражнение 2: Валидация схемы

  1. Создайте файл JSON-схемы для объекта ответа пользователя
  2. Напишите тест, валидирующий схему для эндпоинтов отдельного пользователя и списка
  3. Модифицируйте схему, сделав поле обязательным, и проверьте, что тест обнаружит нарушение
  4. Добавьте валидацию схемы к CRUD-сюите из Упражнения 1

Упражнение 3: Поток аутентификации

  1. Напишите тест аутентификации через POST /auth/login с извлечением JWT-токена
  2. Используйте извлечённый токен для последующих авторизованных запросов
  3. Протестируйте, что неавторизованные запросы возвращают 401
  4. Протестируйте, что просроченные токены возвращают 403 с подходящим сообщением об ошибке