Тестирование API в Playwright/Java. PUT-запросы

Что такое PUT-запрос

PUT-запрос идеально подходит для обновления ресурсов. Поэтому он используется для замены существующих данных. Как и в POST-запросах, заголовок Content-Type играет важную роль для отправки данных в требуемом формате. PUT-запросы обычно возвращают код состояния 200 с обновленными данными в ответе, однако это зависит от требований; некоторые API не возвращают никаких данных в ответе — это зависит от того, как построен ответ этого API. 

Разница между POST и PUT 

Основная разница между PUT и POST-запросами заключается в том, что PUT используется для обновления ресурса, а POST — для создания нового ресурса.

Запрос PUT является идемпотентным, то есть, если один и тот же запрос с одним и тем же телом вызывается несколько раз, он продолжает обновлять ресурс. 

В этой статье приведен пример обновления заказа с помощью запроса PUT, использующего API restful-ecommerce. Этот API (как и в двух предыдущих частях туториала) будет использоваться для демонстрации написания тестов PUT-запросов на Java с помощью Playwright. 

PUT /updateOrder/{id} — Обновляет доступный заказ, используя id заказа.

Этот API принимает id, т.е. order_id, в качестве параметра пути для проверки доступного Заказа. Обновленная информация о заказе должна быть предоставлена в теле запроса, в формате JSON. Важно отметить, что поскольку это PUT-запрос, мы должны отправить полную информацию о заказе, даже если нам нужно обновить только одно поле в заказе. 

Этот API требует предоставления маркера аутентификации (Authentication token), с учетом которого будет обновлен заказ; иначе будет возвращена ошибка, если маркер не предоставлен, или если он недействителен (невалидный).

В случае успешного обновления заказа PUT-запрос возвращает статус-код 200 с обновленной информацией о заказе. 

В случае неудачи обновления возвращаются статус-коды и сообщения об ошибках, основанные на следующих критериях: 

  • Код состояния 400 — Если аутентификация токена не прошла 
  • Код состояния 400 — Если в запросе отправлено неправильное тело или тело отсутствует 
  •  Код состояния 403 — Если  при отправке запроса токен не был предоставлен
  •  Код состояния 404 — Если для соответствующего order_id не найден заказ, доступный для обновления.

Теперь займемся написанием автоматизированных сценариев.

Установка и настройка

Настоятельно рекомендуем ознакомиться с предыдущими частями туториала (PATCH и POST), в которых описываются установка и настройка. 

Тестируемое приложение 

Как и в предыдущих частях, будем использовать Restful-ecommerce API, которые доступны на GitHub по ссылке и бесплатны для использования в учебных целях. Этот проект содержит несколько API, связанных с функциональностью управления заказами в приложении электронной коммерции, эти API позволяют создавать, обновлять, получать и удалять заказы.

Сценарий 1 — Обновление заказа 

  1. Запустить службу restful-ecommerce 
  2. С помощью POST-запроса создать несколько заказов в системе 
  3. Обновить все детали заказа с ID = 2, то есть order_id «2» 
  4. Проверить, что в ответе возвращается статус-код 200 
  5. Проверить, что детали заказа обновлены правильно.

Реализация теста 

Данный сценарий будет реализован в новом тестовом методе testShouldUpdateTheOrderUsingPut(), в существующем тестовом классе HappyPathTests.

    @Test
    public void testShouldUpdateTheOrderUsingPut() {

        final APIResponse authResponse = this.request.post("/auth", RequestOptions.create().setData(getCredentials()));

        final JSONObject authResponseObject = new JSONObject(authResponse.text());
        final String token = authResponseObject.get("token").toString();

        final OrderData updatedOrder = getUpdatedOrder();

        final int orderId = 2;
        final APIResponse response = this.request.put("/updateOrder/" + orderId, RequestOptions.create()
                .setHeader("Authorization", token)
                .setData(updatedOrder));

        final JSONObject updateOrderResponseObject = new JSONObject(response.text());
        final JSONObject orderObject = updateOrderResponseObject.getJSONObject("order");

        assertEquals(response.status(), 200);
        assertEquals(updateOrderResponseObject.get("message"), "Order updated successfully!");
        assertEquals(orderId, orderObject.get("id"));
        assertEquals(updatedOrder.getUserId(), orderObject.get("user_id"));
        assertEquals(updatedOrder.getProductId(), orderObject.get("product_id"));
        assertEquals(updatedOrder.getProductName(), orderObject.get("product_name"));
        assertEquals(updatedOrder.getProductAmount(), orderObject.get("product_amount"));
        assertEquals(updatedOrder.getTotalAmt(), orderObject.get("total_amt"));
    }

При обновлении заказа нужно выполнить следующие 3 шага:

  1. Генерирование маркера аутентификации
  2. Генерирование тестовых данных для обновления заказа 
  3. Обновление заказа с помощью PUT-запроса

Генерация маркера аутентификации 

Эндпойнт POST /authAPI позволит сгенерировать маркер и вернуть сгенерированный маркер в ответе.

<>

Учетные данные для входа в этот API: 

  • username — «admin» 
  • password — «secretPass123» 

Когда в POST-запросе переданы правильные учетные данные, API возвращает статус-код 200 вместе с токеном.

Это значение токена может быть использовано в дальнейшем для выполнения PUT-запроса. Этот токен добавляется в качестве меры безопасности в приложении restful-ecommerce, чтобы только пользователи, знающие учетные данные, то есть доверенные пользователи, могли обновить заказ.

Генерация тестовых данных для обновления заказа

Вторым шагом будет генерация нового набора данных, которые заменят существующие детали заказа. 

Создадим новый метод — getUpdatedOrder() в существующем классе OrderDataBuilder (который мы использовали в части туториала по POST-запросам) для генерации новых тестовых данных заказа.

    public static OrderData getUpdatedOrder() {
        int userId = FAKER.number().numberBetween(4, 5);
        int productId = FAKER.number().numberBetween(335,337);
        int productAmount = FAKER.number().numberBetween(510, 515);
        int quantity = FAKER.number().numberBetween(1, 2);
        int taxAmount = FAKER.number().numberBetween(35,45);
        int totalAmount = (productAmount*quantity)+taxAmount;


        return OrderData.builder()
                .userId(String.valueOf(userId))
                .productId(String.valueOf(productId))
                .productName(FAKER.commerce().productName())
                .productAmount(productAmount)
                .qty(quantity)
                .taxAmt(taxAmount)
                .totalAmt(totalAmount)
                .build();
    }

Этот метод getUpdatedOrder() генерирует совершенно новые данные для заказа, поэтому, когда этот метод вызывается в PUT-запросе, он заменит все существующие данные заказа с этим ID. 

Обновление заказа PUT-запросом

Создадим новый тестовый метод testShouldUpdateTheOrderUsingPut() в существующем тестовом классе HappyPathTests.

    @Test
    public void testShouldUpdateTheOrderUsingPut() {

        final APIResponse authResponse = this.request.post("/auth", RequestOptions.create().setData(getCredentials()));

        final JSONObject authResponseObject = new JSONObject(authResponse.text());
        final String token = authResponseObject.get("token").toString();

        final OrderData updatedOrder = getUpdatedOrder();

        final int orderId = 2;
        final APIResponse response = this.request.put("/updateOrder/" + orderId, RequestOptions.create()
                .setHeader("Authorization", token)
                .setData(updatedOrder));

        final JSONObject updateOrderResponseObject = new JSONObject(response.text());
        final JSONObject orderObject = updateOrderResponseObject.getJSONObject("order");

        assertEquals(response.status(), 200);
        assertEquals(updateOrderResponseObject.get("message"), "Order updated successfully!");
        assertEquals(orderId, orderObject.get("id"));
        assertEquals(updatedOrder.getUserId(), orderObject.get("user_id"));
        assertEquals(updatedOrder.getProductId(), orderObject.get("product_id"));
        assertEquals(updatedOrder.getProductName(), orderObject.get("product_name"));
        assertEquals(updatedOrder.getProductAmount(), orderObject.get("product_amount"));
        assertEquals(updatedOrder.getTotalAmt(), orderObject.get("total_amt"));
    }

Этот метод генерирует токен, создает новый набор обновленных данных заказа, и отправляет PUT-запрос для обновления заказа. Давайте разобьем этот огромный метод на мелкие части, чтобы лучше понять.

Первая часть этого метода генерирует токен. Он использует эндпойнт /auth и отправляет POST-запрос вместе с валидными учетными данными для входа в систему.

getCredentials() — статический метод, который доступен в классе TokenBuilder. Он генерирует и предоставляет валидные учетные данные в формате JSON для использования в POST-запросе /auth.

public class TokenBuilder {

    public static TokenData getCredentials() {
        return TokenData.builder().username("admin")
                .password("secretPass123")
                .build();
    }
}

Метод getCredentials() возвращает объект TokenData, содержащий поля username и password.

@Getter
@Builder
public class TokenData {

    private String username;
    private String password;

}

Ответ будет извлечен и сохранен как JSON-объект в переменной authResponseObject.

Значение токена будет сохранено в переменной token в формате String, которая в дальнейшем будет использоваться в тесте при отправке POST-запросов.

Далее необходимо получить обновленную информацию о заказе. Метод getUpdatedOrder() является статическим и каждый раз при его вызове будет генерироваться новый заказ.

Мы будем использовать заказ с order_id = «2» для обновления деталей заказа. Далее заказ будет обновлен с помощью метода put() в Playwright.

Дополнительные данные, заголовок Authorization и тело запроса будут переданы в параметре метода put().

RequestOptions.create() создаст объект для PUT-запроса и позволит прикрепить заголовок и тело запроса. Метод setHeader() позволит добавить заголовок авторизации, а setData() добавит в запрос объект updatedOrder в качестве тела.

После выполнения запроса ответ обрабатывается и сохраняется в переменной updateOrderResponseObject типа JSONObject

Тело ответа:

{
  "message": "Order updated successfully!",
  "order": {
    "id": 1,
    "user_id": "1",
    "product_id": "1",
    "product_name": "iPhone 15 Pro Max",
    "product_amount": 503,
    "qty": 1,
    "tax_amt": 5.99,
    "total_amt": 508.99
  }
}

Детали заказов извлекаются в объекте «order» в ответе, поэтому мы храним массив в переменной orderObject с типом JSONObject

Последняя часть кода выполняет утверждения (ассерты), чтобы проверить, что детали заказов, полученные в ответе, совпадают с отправленными в теле запроса, и, соответственно, что заказ был обновлен. 

Объект updatedOrder хранит все значения, которые были отправлены в запросе, поэтому он сохраняется как ожидаемый результат; а данные, полученные в ответе, то есть объект orderObject, становятся фактическим результатом.

Выполнение

Нужно сначала создать заказы, прежде чем обновлять их. Поэтому создадим новый файл testng.xmltestng-restfulecommerce-updateorder.xml для выполнения тестов обновления заказов. В этом файле testng.xmlбудут выполняться только два метода, первый из которых — testShouldCreateNewOrders(), а затем метод теста обновления заказа — testShouldUpdateTheOrderUsingPut().

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Restful ECommerce Test Suite">
    <test name="Testing Happy Path Scenarios of Creating and Updating Orders">
        <classes>
            <class name="io.github.mfaisalkhatri.api.restfulecommerce.HappyPathTests">
                <methods>
                    <include name="testShouldCreateNewOrders"/>
                    <include name="testShouldUpdateTheOrderUsingPut"/>
                </methods>
            </class>
        </classes>
    </test>
</suite>

Следующий скриншот выполнения теста показывает, что тесты были выполнены успешно. Сначала были созданы заказы, а затем заказ был обновлен с помощью запроса PUT.

Сценарий 2 — Обновить заказ с невалидным ID

Это так называемый «печальный» сценарий (sad path), в котором мы предоставим неверный order ID при обновлении заказа PUT-запросом.

  1. Используя PUT-запрос, попробуем обновить заказ с недействительным ID, который не существует в системе, например, order_id = 90
  2. Проверим, возвращается ли в ответ код состояния 404 
  3. Проверим, возвращается ли в ответ текст сообщения «Заказ с указанными параметрами не найден!«

Реализация теста 

Мы добавим новый метод testShouldNotUpdateOrder_WhenOrderIdIsNotFound() в существующий класс тестов SadPathTests.

    @Test
    public void testShouldNotUpdateOrder_WhenOrderIdIsNotFound() {
        final APIResponse authResponse = this.request.post("/auth", RequestOptions.create().setData(getCredentials()));

        final JSONObject authResponseObject = new JSONObject(authResponse.text());
        final String token = authResponseObject.get("token").toString();

        final OrderData updatedOrder = getUpdatedOrder();

        final int orderId = 90;

        final APIResponse response = this.request.put("/updateOrder/" + orderId, RequestOptions.create()
                .setHeader("Authorization", token)
                .setData(updatedOrder));


        final JSONObject responseObject = new JSONObject(response.text());

        assertEquals(response.status(), 404);
        assertEquals(responseObject.get("message"), "No Order found with the given Order Id!");

    }

В этом тесте мы отправим жестко прописанный order_id «90», так как нам нужно отправить недействительный ID заказа. 

Реализация этого сценария остается такой же, как и в предыдущем. Все шаги остаются теми же; например, нам нужно сначала сгенерировать токен, затем прикрепить его к заголовку, и использовать метод put() из интерфейса APIRequestContext, чтобы отправить PUT-запрос на обновление заказа.

Для отправки тела запроса будет использоваться тот же статический метод getUpdatedOrder() из класса OrderDataBuilder

Наконец, мы будем предполагать (в ассерте), что в ответ вернется статус-код 404 и текст сообщения «Заказ с заданным ID не найден!«.

Выполнение

Добавим новый блок в файл testng-restfulecommerce-updateorder.xml и выполним его.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Restful ECommerce Test Suite">
    <test name="Testing Happy Path Scenarios of Creating and Updating Orders">
        <classes>
            <class name="io.github.mfaisalkhatri.api.restfulecommerce.HappyPathTests">
                <methods>
                    <include name="testShouldCreateNewOrders"/>
                    <include name="testShouldUpdateTheOrderUsingPut"/>
                </methods>
            </class>
        </classes>
    </test>
    <test name="Testing Sad Path Scenarios of Updating Order">
        <classes>
            <class name="io.github.mfaisalkhatri.api.restfulecommerce.SadPathTests">
                <methods>
                    <include name="testShouldNotUpdateOrder_WhenOrderIdIsNotFound"/>
                </methods>
            </class>
        </classes>
    </test>
</suite>

Следующий скриншот в IntelliJ IDE показывает, что тест был выполнен успешно.

Резюме

Тестирование PUT-запросов важно во время сквозного тестирования, поскольку пользователи гарантированно будут использовать API-запросы PUT для изменения и обновления записей.

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

Тестирование «счастливых» и «печальных» путей позволяет проводить качественное регрессионное тестирование и быстро выявлять дефекты. 

В этом туториале мы запускали тесты в последовательном порядке с помощью testng.xml, поскольку это рекомендуемый способ запуска тестов как локально, так и в CI/CD-конвейере.

M.S.Khatri


Какой была ваша первая зарплата в QA и как вы искали первую работу?

Мега обсуждение в нашем телеграм-канале о поиске первой работы. Обмен опытом и мнения.

Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии

Мы в Telegram

Наш официальный канал
Полезные материалы и тесты
Готовимся к собеседованию
Project- и Product-менеджмент

? Популярное

? Telegram-обсуждения

Наши подписчики обсуждают, как искали первую работу в QA. Некоторые ищут ее прямо сейчас.
Наши подписчики рассказывают о том, как не бояться задавать тупые вопросы и чувствовать себя уверенно в новой команде.
Обсуждаем, куда лучше податься - в менеджмент или по технической ветке?
Говорим о конфликтных ситуациях в команде и о том, как их избежать
$1100*
медианная зарплата в QA в июне 2023

*по результатам опроса QA-инженеров в нашем телеграм-канале

Собеседование

19%*
IT-специалистов переехало или приняло решение о переезде из России по состоянию на конец марта 2022

*по результатам опроса в нашем телеграм-канале

live

Обсуждают сейчас