Ответ
Идемпотентность — это фундаментальное свойство операции в контексте тестирования 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).
Ответ 18+ 🔞
А, идемпотентность! Ну это, блядь, классика, про которую каждый второй архитектор на собеседовании спрашивает, а потом в коде проёбывает. Слушай сюда, сейчас разжую.
Представь себе, что ты в баре. Ты подходишь к бармену и говоришь: «Дай мне стакан воды». Он даёт. Ты, ёпта, через пять минут опять: «Дай мне стакан воды». Он даёт такой же стакан. Это идемпотентность. А теперь представь, что ты говоришь: «Налей мне ещё стакан воды». Он наливает. Ты опять: «Налей мне ещё стакан воды». Он наливает второй. А ты уже не хочешь, у тебя уже два стакана, а он третий льёт. Вот это пиздец, это уже не идемпотентность. Понимаешь разницу? В первом случае результат — один стакан в твоей руке, сколько раз ни проси. Во втором — овердохуища стаканов, и ты уже тонешь.
Так вот, в API та же херня. Нам, тестировщикам, это надо понимать, чтобы не охуеть от результатов, когда сеть глючит и запросы летят по три раза.
Смотри, табличка, которую надо выучить, как «Отче наш», а то будешь выглядеть как манда с ушами на ретро:
- GET — идемпотентный. Это как спросить «который час?». Спрашивай хоть сто раз — время-то одно и то же (пока стрелки не сдвинутся, но это уже другая операция). Проверяй, что кэш не ебёт мозги.
- POST — НЕ идемпотентный, мать его. Это как сказать «роди мне ребёнка». Скажешь два раза — получишь близнецов, скажешь пять — получишь баскетбольную команду. Самый частый источник говна в тестах — дубли из-за повторных POST. Доверия к нему — ноль ебать.
- PUT — идемпотентный. Это как команда «поставь чашку на стол». Поставил — стоит. Ещё раз сказал «поставь чашку на стол» — она так и стоит на том же месте, не взлетает. Проверяй, что последняя команда победила, а не создала вторую чашку из воздуха.
- DELETE — идемпотентный, но хитрый. Первый раз говоришь «выбрось чашку» — она летит в урну. Второй раз говоришь — а её уже нет. Результат разный (сначала
200 OK, потом404), но состояние системы после первого вызова не меняется: чашки нет. Это ок. - PATCH — обычно НЕ идемпотентный. Это как сказать «долей в чашку воды». Скажешь раз — долил, скажешь два — перелил, скажешь три — потоп устроил. Подозрение ебать чувствую к этому методу.
Вот тебе пример, как проверить, что PUT не обманывает. Смотри код, его не трогаю, он святой.
@Test
public void testPutMethodIsIdempotent() {
// Готовим нашего подопытного пользователя
String userJson = "{"name": "TestUser", "email": "test@example.com"}";
// Первый залп. Создаём/апдейтим юзера с id 123.
given()
.contentType("application/json")
.body(userJson)
.when()
.put("/api/users/123")
.then()
.statusCode(200)
.body("name", equalTo("TestUser"));
// А теперь, внимание, хуй с горы! Шлём ТОЧНО ТАКОЙ ЖЕ запрос ещё раз.
given()
.contentType("application/json")
.body(userJson)
.when()
.put("/api/users/123")
.then()
.statusCode(200) // Должен быть 200, а не 201 Created второй раз! Иначе пизда рулю.
.body("name", equalTo("TestUser")); // Имя должно остаться TestUser, а не превратиться в TestUserUser.
// Финальный аккорд. Дёргаем GET и убеждаемся, что всё на месте, как и должно быть.
when()
.get("/api/users/123")
.then()
.body("name", equalTo("TestUser"));
}
А что делать с этим ебанько POST, который плодит сущности как сука? Тут надо хитрить.
- Уникальность. Пихай в каждый тестовый запрос что-то уникальное: timestamp, случайный UUID. Типа
"email": "test_" + System.currentTimeMillis() + "@example.com". Чтобы второй запрос просто не прошёл по уникальности. - Уборка за собой. Перед тестом создал — после теста прибил. Или перед следующим прогоном всю базу тестовую в ноль. Чистота — залог ахуенных и стабильных тестов.
- Проверка на адекватность. Если всё-таки шлёшь два одинаковых POST, сервер должен тебе вежливо, но жёстко сказать: «Чувак, ты чё, такое уже есть» — и вернуть ошибку
409 Conflict. Если он вместо этого создаёт дубликат — это повод написать баг-репорт размером с небольшую повесть.
Короче, запомни: идемпотентность — это когда от твоего запроса не должно быть мультиков, как от неконтролируемого POST. Один раз — и готово. Как горох об стенку.
Видео-ответы
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶
▶