Ответ
Идемпотентность — это фундаментальное свойство операции в контексте тестирования API и распределенных систем. Операция считается идемпотентной, если многократное выполнение одной и той же операции с одинаковыми входными данными приводит к одному и тому же результату, что и однократное выполнение.
Почему это критически важно для QA-инженера?
- Отказоустойчивость и повторные запросы: Клиент (или тест) может безопасно повторять идемпотентный запрос при таймаутах, сетевых сбоях, не получая побочных эффектов (например, создания дубликатов).
- Предсказуемость тестов: Идемпотентные операции делают тесты более стабильными и воспроизводимыми.
| Идемпотентность HTTP-методов (с точки зрения тестировщика): | Метод | Идемпотентен? | Объяснение и что проверять |
|---|---|---|---|
| GET | Да | Получение данных. 10 одинаковых GET /users/1 должны вернуть один и тот же ответ (если данные не менялись иным способом). Проверяем кэширование. |
|
| POST | Нет | Создание ресурса. Повторный POST /users с телом {"name":"John"} создаст второго пользователя с тем же именем (дубликат). Это частая причина багов. |
|
| PUT | Да | Полное обновление. PUT /users/1 с телом {"name":"Alice"}. Сколько бы раз мы его ни отправили, в итоге у пользователя с id=1 будет имя "Alice". Проверяем, что последний запрос "побеждает". |
|
| DELETE | Да | Удаление. Первый DELETE /users/1 удалит пользователя и вернет 200 или 204. Последующие вызовы должны возвращать 404 (Not Found) или 410 (Gone) — результат разный, но состояние системы не меняется после первого вызова. Это все равно идемпотентность. |
|
| PATCH | Нет (обычно) | Частичное обновление. Зависит от реализации. PATCH /users/1 с {"balance": +10} при каждом вызове будет увеличивать баланс на 10. Это неидемпотентно. |
Пример теста на проверку идемпотентности метода PUT:
// Пример на Java с RestAssured
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
@Test
public void testPutMethodIsIdempotent() {
String userJson = "{"name": "TestUser", "email": "test@example.com"}";
// 1. Первый PUT-запрос (создание/обновление)
given()
.contentType("application/json")
.body(userJson)
.when()
.put("/api/users/123")
.then()
.statusCode(200) // или 201 Created
.body("name", equalTo("TestUser"));
// 2. Второй ИДЕНТИЧНЫЙ PUT-запрос
given()
.contentType("application/json")
.body(userJson) // То же самое тело
.when()
.put("/api/users/123")
.then()
.statusCode(200) // Должен быть тот же статус, что и в первый раз (не 201)
.body("name", equalTo("TestUser")); // Имя должно остаться "TestUser", а не измениться
// 3. Дополнительная проверка: GET после двух PUT должен вернуть те же данные
when()
.get("/api/users/123")
.then()
.body("name", equalTo("TestUser"));
}
Как тестировать неидемпотентные операции (например, POST)?
- Использовать уникальные данные (timestamp, UUID) в каждом тестовом прогоне.
- Реализовать механизм очистки тестовых данных перед/после теста.
- Проверять, что сервер возвращает корректные ошибки при попытке создать дубликат (например,
409 Conflict).
Видео-ответы
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶